Pattern matching against mutable references

I'm new to Rust. I would like to know why &item can't be used in the second for loop below. I was expecting that with &item I'd get a mutable variable: item.

 let mut collection = vec![1, 2, 3];
 for &item in collection.iter() {} // ok
 for &item in collection.iter_mut() {} // error: mismatched types expected `&mut {integer}` found `&_`

Looks like pattern matching won't work for mutable references?

rustc 1.60.0 (7737e0b5c 2022-04-04)

You need to use &mut when pattern matching against a mutable reference.

 for &mut item in collection.iter_mut() {}
4 Likes

Thank you !!

1 Like

Related query:
Any reason why variable y in the below code is not mutable?

  let mut x = 1;
  let &mut y = &mut x;
  y = 2; // error: cannot assign twice to immutable variable `y`

The statement let &mut y = &mut x just takes the &mut x reference and bind y to the value behind it, i.e. 1 (the i32, not &mut i32).

Phrased differently, your code is equivalent to this

let mut x = 1;
let y: i32 = *(&mut x); 
y = 2;

If you want y to be mutable, you would add a mut before the variable being bound (i.e. y) to get this awkward statement

fn main() {
    let mut x = 1;
    let &mut mut y = &mut x;
    y = 2;
    println!("y: {y}");
    println!("x: {x}");
}

(playground)

Which generates this output

y: 2
x: 1

Note that x and y have different values here because the &mut mut y destructuring moved the 1 out from behind the &mut x (which just copies the value because i32: Copy) and bound it to a new i32 variable, y.

Have a play with that playground link and see if you can predict what would happen if you changed various things.

4 Likes

I just spotted an interesting ambiguity in the pattern grammar here: & mut y could theoretically mean two different things:

  1. An immutable binding y to the target of a mutable reference, or
  2. A mutable binding y to the target of an immutable reference.

In practice, it appears to always be interpreted as (1), so this code generates a compile error:

fn main() {
    let x = 1;
    let & mut y = &x;
    y = 2;
    println!("y: {y}");
    println!("x: {x}");
}

Whereas this compiles fine, with only a warning about an unused assignment:

fn main() {
    let x = 1;
    let & (mut y) = &x;
    y = 2;
    println!("y: {y}");
    println!("x: {x}");
}
7 Likes

I don't think this is technically an ambiguity - meaning, it's unambiguous according to the language grammar because we read the &mut first then look for either another pattern or the IdentifierPattern (i.e. ref? mut? ident) - but it's definitely unintuitive for a human.

Following the same rules, Rust-analyzer gives me this syntax tree for the &mut y version.

LET_STMT@36..56
  LET_KW@36..39 "let"
  REF_PAT@40..46
    AMP@40..41 "&"
    MUT_KW@41..44 "mut"
    IDENT_PAT@45..46
      NAME@45..46
        IDENT@45..46 "y"
  EQ@47..48 "="
  ...
  SEMICOLON@55..56 ";"

While I get a different parse tree for let &(mut y).

LET_STMT@36..54
  LET_KW@36..39 "let"
  REF_PAT@40..48
    AMP@40..41 "&"
    PAREN_PAT@41..48
      L_PAREN@41..42 "("
      IDENT_PAT@42..47
        MUT_KW@42..45 "mut"
        NAME@46..47
          IDENT@46..47 "y"
      R_PAREN@47..48 ")"
  ...
  SEMICOLON@53..54 ";"

You can see how the mut y bit is nested inside a PAREN_PAT (their name for the GroupedPattern rule).

2 Likes

The parens do force a different parse tree for the second version, but the grammar rules appear to be ambiguous¹. In both cases, we're talking about a ReferencePattern which then contains an IdentifierPattern (both of which are alternates for PatternWithoutRange:

ReferencePattern :
(& |&& ) mut ? PatternWithoutRange

IdentifierPattern :
ref ? mut ? IDENTIFIER (@ PatternNoTopAlt ) ?

The ambiguity here lies in which of these two rules the mut belongs to. Substituting the IdentifierPattern rule into ReferencePattern gives us this construction:

(& |&& ) mut ? ref ? mut ? IDENTIFIER (@ PatternNoTopAlt ) ?

As there is no ref token present, we have one mut token to match against (mut? mut?), and each of those positions carries a different semantic meaning.


¹ As long as that's a BNF-style (generative) grammar and not a PEG-style grammar

2 Likes

I started an IRLO thread for the more technical grammar discussion, in the hopes of not derailing this one too much.

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.