Hmm. This is a case where you have to spend a little effort to get around the borrow rules. Normally, if you see a pattern like this:
match h.get(&k) {
Some(val) => *val,
None => {
let val = ...;
h.insert(k, val);
val
}
}
The answer is to use the entry
API to get an entry record, which represent a place in in a hash table where an item could be, and allows you to call or_insert
or or_insert_with
to insert a value for missing keys. This winds up being quite convenient for many cases where you see this pattern, for instance if you want to initialize with 0 and increment every time, you would do:
*h.entry(k).or_insert(0) += 1;
However, in this case, this doesn't help too much, because you would need to use or_insert_with
to compute a value to insert, but that closure would need to re-borrow the hashmap, which it can't because the Entry
already has it borrowed:
*h.entry(n).or_insert_with(||
1 + match n % 2 {
0 => count_collatz(n / 2, h),
_ => count_collatz(n * 3 + 1, h)
}
)
The above fails with the error "error: closure requires unique access to h
but *h
is already borrowed."
So, I think to do this you're going to need to break apart your borrows into a couple of distinct pieces. One for getting the value out if it is present, one for doing the recursive computation, and one for adding the result of the computation to the hashmap. Luckily if let
makes this not too painful; the borrow upon getting the value only lasts within the if let
, so afterwards you can continue to use the reference to recursively compute and insert the new value.
fn count_collatz(n: usize, h: &mut HashMap<usize, usize>) -> usize {
if n == 1 {
return 1;
}
if let Some(val) = h.get(&n) {
return *val;
}
let val = 1 + match n % 2 {
0 => count_collatz(n / 2, h),
_ => count_collatz(n * 3 + 1, h)
};
h.insert(n, val);
val
}