Rust warns me that the variable does not need to be mutable, but I mutate it right after the declaration

Below, I declare cards as mutable and I mutate the value by popping the last element. However, the compiler warns me that it does not need to be mutable and if I delete mut keyword it still compiles.
How and why I am allowed to mutate an immutable variable in this context?

    pub fn get(&self, bayi_id: &str, table_id: &str) -> Option<usize> //{{{
    {
        let mut frees = self.free_cards.write().unwrap();
        let tables = frees.entry(bayi_id.to_string()).or_default();
        let mut cards = tables.entry(table_id.to_string()).or_default();
        cards.pop()
    }

cards is already a mutable reference, being of type &mut Vec<...>. It’s a bit perplexing, I agree, but you don’t need a let mut binding to use a &mut self method on them. You would need a let mut if you wanted to reassign cards to a different &mut Vec<...>.

2 Likes

Mutation in Rust is done (ignoring Interior Mutability patterns using, for instance, Cells or atomics) through unique references: &mut _.

The mut binding modifier, as in:

  • let binding:

    let mut varname = expr;
    let (x, mut y) = (..., ...); // irrefutable pattern / destructuring pattern match
    
  • match and if let patterns:

    match expr { mut varname => { ... } }
    match expr { Some(mut varname) => { ... }, _ => { ... }}
    if let Some(mut varname) = expr { ... } else { ... }
    
  • function parameters:

    fn foo (mut varname, ...) -> ... { ... }
    fn method (mut self, ...) -> ... { ... }
    fn bar ((x, mut y), ...) -> ... { ... } // same destructuring patterns as with `let` bindings
    

is used to allow the programmer to take a &mut _ unique reference out of it (I call it “unique-awareness”):

  • either explicitely with the &mut varname construct;

  • or implicitly,

    • with the . dot operator and its autoborrow (e.g., some_string.push_str(...)),

    • or with syntactic sugar, like mutation assignment’s: varname = ...; (sugar for *(&mut varname) = ...;), or other operation sugar (e.g., some_string += ...);

  • and recursively on indexing or field accessing: varname.some_field = ...,
    slice_of_strings[0].push_str(...).

Once you have a &mut _ reference, like the returned value of HashMap's Entry's or_default(), you no longer need a mut modifier on the binding (unless you intend on mutating (overriding) the pointer itself rather than the pointee, which is very rare (I’ve only ended up doing that when manually iterating through linked lists)).

1 Like