Update a mutable in the else scope

Why can I not update the Vector inside the hash here?
I'm getting:

cannot borrow data in a & reference as mutable

fn main() {
          let text = "
  
  # section 1
  blah blah
  qwerty
  
  # section 2
  zxcvbn
  woup dee woup
  ";
  
      let lines = &text.lines().collect();
      parse(lines);
  }
  
  use std::collections::HashMap;
  fn parse(lines: &Vec<&str>) {
      let mut started = false;
      let mut langs = HashMap::new();
      let mut k: &str;
  
      for line in lines {
          if line.starts_with("# ") {
              started = true;
              k = line[2..].trim();
              let v = vec![*line];
              langs.insert(k, v);
          } else {
              if started {
                langs[k].push(*line)
              } else {
                  continue;
              }
          }
      }
      for (k, v) in langs.iter() {
          println!("{}:", k);
          for w in v.iter() {
              println!("\t{}", w);
          }
      }
  }
1 Like

Use .get_mut(k).unwrap() (to .push(*line)) rather than [k] since HashMap does not seem to implement the IndexMut trait (it only implements Index, which only yields shared references).

  • As to why that is, I don't know; it looks like an oversight.
1 Like

That is to prevent bugs, for example,

Should &mut map[el] make a new element if el is not in the hashmap?
How about map[el] = value;?

If your answers differed, then Rust can't implement IndexMut for HashMap because there is no way to distinguish between the two cases. Inserting on a mutable borrow is a footgun, because it differs from simply doing map.get_mut(el).unwrap(), this difference would make it prone to bugs.

3 Likes

Oh true, I see how many people would expect such syntax to insert value in the [el] slot if there weren't any.

(although the same could be said of Vec and someone writing let len = v.len(); v[len] = value)

I should take the opportunity to mention that for mutation, there's also the entry interface when you want to be able to manipulate a key's entry in the map, with the ability to determine if it's occupied or not, with useful functions to insert and update the values.

This lets you say succinct things like m.entry(k).or_default().push(*x).

3 Likes