Can't push to a Vector

Hello,
I'm learning rust, in the end of chapter 8.3, there's some suggested projects to build. I'm doing the last one which is:

Using a hash map and vectors, create a text interface to allow a user to add employee names to a department in a company; for example, “Add Sally to Engineering” or “Add Amir to Sales.” Then let the user retrieve a list of all people in a department or all people in the company by department, sorted alphabetically.

but when I'm trying to push a name to the vector in the hashmap, I get the following error:

error[E0614]: type `()` cannot be dereferenced
  --> src/main.rs:12:25
   |
12 |         .and_modify(|e| *e.push(name))
   |                         ^^^^^^^^^^^^^ can't be dereferenced

For more information about this error, try `rustc --explain E0614`.

and If I remove the * the error gets weirder:

error: lifetime may not live long enough                                                                                                                                                    
  --> src/main.rs:10:5                                                                                                                                                                      
   |                                                                                                                                                                                        
 9 |   fn add(departments: &mut HashMap<&str, Vec<&str>>, department_name: &str, name: &str) -> () {                                                                                        
   |                                    -                                  - let's call the lifetime of this reference `'1`                                                                 
   |                                    |                                                                                                                                                   
   |                                    let's call the lifetime of this reference `'2`                                                                                                      
10 | /     departments                                                                                                                                                                      
11 | |         .entry(department_name)                                                                                                                                                      
   | |_______________________________^ argument requires that `'1` must outlive `'2`                                                                                                        
   |                                                                                                                                                                                        
   = note: requirement occurs because of a mutable reference to `HashMap<&str, Vec<&str>>`                                                                                                  
   = note: mutable references are invariant over their type parameter                                                                                                                       
   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance                                                                                       
help: consider introducing a named lifetime parameter                                                                                                                                       
   |                                                                                                                                                                                        
 9 | fn add<'a>(departments: &mut HashMap<&'a str, Vec<&str>>, department_name: &'a str, name: &str) -> () {                                                                                
   |       ++++                            ++                                    ++                                                                                                         
                                                                                                                                                                                            
error: lifetime may not live long enough                                                                                                                                                    
  --> src/main.rs:10:5                                                                                                                                                                      
   |                                                                                                                                                                                        
 9 |   fn add(departments: &mut HashMap<&str, Vec<&str>>, department_name: &str, name: &str) -> () {                                                                                        
   |                                              -                                    - let's call the lifetime of this reference `'3`                                                     
   |                                              |                                                                                                                                         
   |                                              let's call the lifetime of this reference `'4`                                                                                            
10 | /     departments                                                                                                                                                                      
11 | |         .entry(department_name)                                                                                                                                                      
   | |_______________________________^ argument requires that `'3` must outlive `'4`                                                                                                        
   |                                                                                                                                                                                        
   = note: requirement occurs because of a mutable reference to `HashMap<&str, Vec<&str>>`                                                                                                  
   = note: mutable references are invariant over their type parameter                                                                                                                       
   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance                                                                                       
help: consider introducing a named lifetime parameter                                                                                                                                       
   |                                                                                                                                                                                        
 9 | fn add<'a>(departments: &mut HashMap<&str, Vec<&'a str>>, department_name: &str, name: &'a str) -> () {                                                                                
   |       ++++                                      ++                                      ++                                                                                             
                                                                                                                                                                                            
error: could not compile `company_map` (bin "company_map") due to 2 previous errors

Here's my code:

use std::collections::HashMap;
fn main() {
    let mut departments: HashMap<&str, Vec<&str>> = HashMap::new();
    add(&mut departments, "Engineering", "Amir");
    println!("{departments:?}")
}

fn add(departments: &mut HashMap<&str, Vec<&str>>, department_name: &str, name: &str) -> () {
    departments
        .entry(department_name)
        .and_modify(|e| *e.push(name))
        .or_insert(vec![name]);
}

Thanks!

lifetimes are part of the type system, you need to annotate the function to satisify the borrow checker. if you omit the lifetime annotation, the default elision rule will not work for this example.

the correct signature should look like this:

fn add<'a>(departments: &mut HashMap<&'a str, Vec<&'a str>>, department_name: &'a str, name: &'a str) -> () {
    departments
        .entry(department_name)
        .and_modify(|e| e.push(name))
        .or_insert(vec![name]);
}

Thanks for your help! I solved my problem by using Strings instead of &strs.
But not sure which solution is more idiomatic, because I'm a beginner.
Can you help?

It depends on whether you need to move around the hashmap or not. In Rust, knowing this distinction is crucial when designing your APIs.

By the way, using .and_modify().or_insert() is not a good way to work with a hash_map::Entry. When adding/appending things, you can use or_default() to insert an empty container when needed:

    departments
        .entry(department_name)
        .or_default()
        .push(name);

The advantage of this, besides just being simpler, is that instead of having separate paths for the first element and the rest of the elements, all elements are inserted the same way, so there are less likely to be bugs introduced later by changing only one part.

And, if you do have to do a different thing based on whether there is an entry in the map already, match departments.entry(..) {...} is clearer and more flexible. In my opinion, .and_modify().or_insert() should never be used.

6 Likes