Idiomatic way to add HashMap insert helper function

I have a HashMap where the key is derived from the value, so I want to not reproduce that derivation code every time.
Local functions don't work since they cannot access the local variable, and a closure fails as soon as anything else wants to use the map.
Here a simplified example:

use std::collections::HashMap;

fn main() {
    let mut map: HashMap<usize, String> = HashMap::new();
    let mut add_string = |string: String| {
        map.insert(string.len(), string);
    };
    add_string("hi".to_string());
    map.get(&1);
    add_string("ho".to_string());
}

Which produces:

error[E0502]: cannot borrow `map` as immutable because it is also borrowed as mutable
  --> src/bin/question.rs:11:9
   |
5  |     let mut add_string = |string: String| {
   |                          ---------------- mutable borrow occurs here
6  |         map.insert(string.len(), string);
   |         --- first borrow occurs due to use of `map` in closure
...
10 |         add_string("ho".to_string());
   |         ---------- mutable borrow later used here
11 |         map.get(&1);
   |         ^^^ immutable borrow occurs here

The alternative I found is to define and implement a trait:

use std::collections::HashMap;

fn main() {
    let mut map: HashMap<usize, String> = HashMap::new();
    map.add_string("hi".to_string());
    map.get(&1);
    map.add_string("ho".to_string());
}

trait InsertString {
    fn add_string(&mut self, event: String);
}
impl InsertString for HashMap<usize, String> {
    fn add_string(&mut self, event: String) {
        self.insert(event.len(), event);
    }
}

The syntax inside main() is fine, but I find those 8 lines below adding too much noise.

For example, in Kotlin I could accomplish similar with an extension function without any noise:

fun HashMap<Int, String>.insert(string: String) =
    this.insert(string.length, string)

Is there not a more idiomatic way than defining a specific trait for one-time use?
I started learning Rust a few days ago after years in OOP and a bit of functional programming experience,
so feel free to correct me if there is something totally different I should be doing.

I also found the possibility of passing a mutable reference, but again the code seems kinda noisy:

    let mut add_string = |map: &mut HashMap<usize, String>, string: String| {
        map.insert(string.len(), string);
    };
    add_string(&mut map, "hi".to_string());
    add_string(&mut map, "h0".to_string());

Extension traits are perfectly idiomatic.

If you don't want a trait, why don't you just write a free function?

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.