Why is ref mut needed here?

Why is ref mut needed below? Can't seem to find much info on the pattern, just followed the compilers error instructions to fix it.

fn main() {
    let mut x = [1,2,3];
    let y = &mut x;
    let mut z = Some(y);
    
    // Fails to compile if remove the ref mut below
    if let Some(ref mut za) = z {
        za[0] = 98;
    }
    
    if let Some(zb) = z {
        zb[0] = 99;
    }

}

Kind Regards

By default, identifier patterns bind a variable to a copy of or move from the matched value depending on whether the matched value implements Copy. This can be changed to bind to a reference by using the ref keyword, or to a mutable reference using ref mut. For example:

match a {
    None => (),
    Some(value) => (),
}

match a {
    None => (),
    Some(ref value) => (),
}

In the first match expression, the value is copied (or moved). In the second match, a reference to the same memory location is bound to the variable value.

src: https://doc.rust-lang.org/reference/patterns.html#identifier-patterns

With match ergonomics (I believe that was what the change is called?) this can be written without the ref mut, so that the above rules move the &mut z by value, therefore producing a mutable reference to the contents of the option.

    if let Some(za) = &mut z {
        za[0] = 98;
    }

Edit: This is my guess, on mobile so difficult to test it with this change. Capturing &mut x in y still means that you're moving out of z (since &mut is not Copy)

You might check if z.as_mut() works in this case as well.

1 Like

Yes, the two forms are equivalent. The ref mut form is the original and the &mut form is some extra magic that tends to be more intuitive. In the OP's case it demonstrates more clearly that the first if let performs a mutable borrow while the second if let performs a move, and hence why it is required.

I don't know if I recommend it over the other forms, but you can perform a reborrow of the &mut inside of the Some (i.e. avoid moving it) like so:

    if let Some(&mut ref mut za) = z {
        za[0] = 98;
    }

This mirrors what happens automatically if you do

// `*y` is reborrowed and not moved
let ya: &mut _ = y;
// or
let ya = &mut *y;

Note how z no longer needs to be a mut binding (since we no longer create a &mut to it).

1 Like