rust 115 lines · 4 tabs

Detect Duplicate Files with Parallel Blake3 Checksums in Rust

Shared by codesnips Sep 2026
4 tabs
use std::collections::HashMap;
use std::path::PathBuf;

use walkdir::WalkDir;

pub fn group_by_size(root: &str) -> HashMap<u64, Vec<PathBuf>> {
    let mut by_size: HashMap<u64, Vec<PathBuf>> = HashMap::new();

    for entry in WalkDir::new(root).follow_links(false) {
        let entry = match entry {
            Ok(e) => e,
            Err(err) => {
                eprintln!("skip: {}", err);
                continue;
            }
        };

        if !entry.file_type().is_file() {
            continue;
        }

        let len = match entry.metadata() {
            Ok(meta) => meta.len(),
            Err(_) => continue,
        };

        by_size.entry(len).or_default().push(entry.into_path());
    }

    by_size
}
4 files · rust Explain with highlit

This snippet implements a two-phase duplicate-file detector, a pattern used by backup tools and disk cleaners to find byte-identical files across a directory tree. Hashing every file is expensive, so the design leans on a cheap pre-filter: files can only be duplicates if they share the same size. scanner.rs walks the tree and groups paths by length, and only groups with more than one member are ever hashed.

In scanner.rs, group_by_size uses walkdir::WalkDir to iterate the tree, skips anything that is not a regular file, and folds paths into a HashMap<u64, Vec<PathBuf>> keyed on metadata.len(). Symlinks are ignored via follow_links(false) so the same inode is not counted twice and cycles cannot trap the walk. The returned map still contains singleton size-buckets; those are cheap to discard later and keeping them keeps this function pure and testable.

hasher.rs holds the actual content hashing. hash_file streams the file through a fixed 64 KiB buffer into a blake3::Hasher, which avoids loading large files fully into memory. Blake3 is chosen over MD5/SHA for speed and because it is cryptographically strong enough that a hash collision is not a practical concern, so matching digests are treated as identical content without a fallback byte comparison. The digest is returned as a hex String for easy use as a map key.

dedup.rs ties the phases together and adds parallelism. find_duplicates first calls group_by_size, then uses rayon's par_iter to hash candidate files concurrently — I/O and CPU-bound hashing scale well across cores here. Files are collected into a HashMap<String, Vec<PathBuf>> keyed by digest, and retain drops any digest seen only once, leaving true duplicate sets. Errors from unreadable files are logged and skipped rather than aborting the whole run, which matters when scanning system directories with permission gaps.

The main trade-off is that the size pre-filter assumes duplicates share exact byte length, which is always true for identical files, making it a safe optimization. The approach reaches for parallel hashing only where it pays off, keeping the common case of unique-sized files nearly free.


Related snips

Share this code

Here's the card — post it anywhere.

Detect Duplicate Files with Parallel Blake3 Checksums in Rust — share card
Link copied