Reputation: 189
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.
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
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