0

Is the rust compiler being too conservative by preventing a "simultaneous" borrow and mutable borrow in two separate branches of the function?

What is the correct way to do this?

use std::collections::HashMap;
use core::hash::Hash;

fn f<K: Eq + Hash, V>(map: &mut HashMap<K, Option<V>>, key: K, default: V) -> &V {
    if let Some(result) = map.entry(key).or_default() {
        return result
    } else {
        map.insert(key, Some(default));
        return &default
    }
}

Errors:

error[E0499]: cannot borrow `*map` as mutable more than once at a time
 --> src/lib.rs:8:9
  |
4 | fn f<K: Eq + Hash, V>(map: &mut HashMap<K, Option<V>>, key: K, default: V) -> &V {
  |                            - let's call the lifetime of this reference `'1`
5 |     if let Some(result) = map.entry(key).or_default() {
  |                           -------------- first mutable borrow occurs here
6 |         return result
  |                ------ returning this value requires that `*map` is borrowed for `'1`
7 |     } else {
8 |         map.insert(key, Some(default));
  |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ second mutable borrow occurs here

error[E0382]: use of moved value: `key`
 --> src/lib.rs:8:20
  |
4 | fn f<K: Eq + Hash, V>(map: &mut HashMap<K, Option<V>>, key: K, default: V) -> &V {
  |                                                        --- move occurs because `key` has type `K`, which does not implement the `Copy` trait
5 |     if let Some(result) = map.entry(key).or_default() {
  |                                     --- value moved here
...
8 |         map.insert(key, Some(default));
  |                    ^^^ value used here after move
  |
help: consider further restricting this bound
  |
4 | fn f<K: Eq + Hash + std::marker::Copy, V>(map: &mut HashMap<K, Option<V>>, key: K, default: V) -> &V {
  |                   +++++++++++++++++++

error[E0515]: cannot return reference to function parameter `default`
 --> src/lib.rs:9:16
  |
9 |         return &default
  |                ^^^^^^^^ returns a reference to data owned by the current function

error[E0382]: borrow of moved value: `default`
 --> src/lib.rs:9:16
  |
4 | fn f<K: Eq + Hash, V>(map: &mut HashMap<K, Option<V>>, key: K, default: V) -> &V {
  |                                                                ------- move occurs because `default` has type `V`, which does not implement the `Copy` trait
...
8 |         map.insert(key, Some(default));
  |                              ------- value moved here
9 |         return &default
  |                ^^^^^^^^ value borrowed here after move
  |
help: consider restricting type parameter `V`
  |
4 | fn f<K: Eq + Hash, V: std::marker::Copy>(map: &mut HashMap<K, Option<V>>, key: K, default: V) -> &V {
  |                     +++++++++++++++++++

I know that this can be done more elegantly via the entry api. The real example isn't a hashmap. I've made a contrived example that uses only the following operations:

  1. get a value option from a key
  2. insert an key, value
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Test
  • 962
  • 9
  • 26
  • 2
    Your example has a whole bunch of errors, as the rustc output says, many unrelated to borrows of `map`. If this is just a toy example, can you make an example instead where the errors are more focused on what you want to ask about? – Dan Getz Sep 22 '22 at 01:59

0 Answers0