ppenguin
ppenguin

Reputation: 189

"my first rust": the method `unwrap_or_default` exists for enum `Option<&mut ...>`, but its trait bounds were not satisfied

My very first rust program/function is supposed to walk a dir tree and deliver a HashMap with K(mime_as_string) and V(count_sum, size_sum). And I want it in FP style.

So what I have is this:

fn files_info4(rootpath: &str) {

    struct FTypeStats {
        count: u64,
        size: u64,
    }

    impl Default for FTypeStats {
        fn default() -> Self {
            FTypeStats {
                count: 0,
                size: 0,
            }
        }
    }

    // get file type stats directly/functionally/lazy
    let fts = WalkDir::new(rootpath)
    .min_depth(1)
    .max_depth(99)
    .into_iter()
    .filter_map(|entry| entry.ok())
    .map(|entry| (entry, entry.metadata().map_or(false, |m| m.is_file())))
    .filter(|(e, f)| *f)
    .fold(HashMap::new(), | mut acc: HashMap<String, FTypeStats>, (e, _) | {
        let ftype = tree_magic::from_filepath(e.path());
        acc.get_mut(&ftype).unwrap_or_default().count += 1;
        acc.get_mut(&ftype).unwrap_or_default().size += e.metadata().map_or(0, |m| m.len());
        acc
    });
}

where I did the impl Default after meeting the error:

error[E0599]: the method `unwrap_or_default` exists for enum `Option<&mut FTypeStats>`, but its trait bounds were not satisfied
  --> src/main.rs:54:29
   |
54 |         acc.get_mut(&ftype).unwrap_or_default().count += 1;
   |                             ^^^^^^^^^^^^^^^^^ method cannot be called on `Option<&mut FTypeStats>` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `&mut FTypeStats: Default`

But it still didn't help, i.e. the same error remains.

Questions:

  1. how to make it compile/run
  2. can the evaluation chain be optimised? (And is the code within fold actually correct regarding e.g. the assignments I need for FTypeStats and initialization for the default/initial values? (It feels a bit "implicit"))

TIA!

Upvotes: 0

Views: 1143

Answers (1)

user2722968
user2722968

Reputation: 16475

There are multiple problems here: First, calling .get_mut() on the HashMap gives you an Option which is None in case the key is not in the map. The Option itself is detached from the map (aside from lifetime constraints), so in the None-case, there is no link back into the map that would put a constructed-from-default value in it's proper place.

Second, as the error message says, the method unwrap_or_default() requires the inner type of Option to actually implement Default, in this case &mut FTypeStats. Not only does such an implementation not exist in your code, but it would also be very hard to do so because generating a (valid) mutable reference to some type out of thin air - which is the whole point of Default - is practically impossible.

The solution is to use the entry() API on the HashMap instead of get_mut():

*acc.entry(&ftype).or_default().count += 1;

The call to entry() gives you a proxy-value to wherever &ftype is - or would be - in the HashMap. The call to or_default() always gives you a &mut FTypeStats. If &ftype is not in the map, it uses Default on FTypeStats (not Default on &mut FTypeStats) to generate a new entry, inserts it, and gives you a mutable reference to that.

Upvotes: 2

Related Questions