Read reference from Option<&mut T> multiple times

I have an Option<&mut T> and want to access the contained reference multiple times, like so:

fn f(a: Option<&mut i32>) {
    if let Some(x) = a {
        *x = 6;
    }
    // ...
    if let Some(x) = a {
        *x = 7;
    }
}

fn main() {
    let mut x = 5;
    f(Some(&mut x));
}

That doesn't work, because if let Some(x) = a moves the reference value out of the Option, and the second if let Some(x) = a will result in a compiler error. Without the second if let ..., this works flawlessly, so a doesn't have to be mutable.

The following:

if let Some(ref x) = a {
    **x = 6;
}

gives an error: "assignment into an immutable reference".

This would work:

fn f(mut a: Option<&mut i32>) {
    if let Some(ref mut x) = a {
        **x = 6;
    }
    if let Some(ref mut x) = a {
        **x = 7;
    }
}

The mut a is necessary, otherwise I get an error "cannot borrow immutable anonymous field (a:std::prelude::v1::Some).0 as mutable". But this feels wrong: a shouldn't have to be mutable, because I'm not modifying it (see above).

What's the correct solution?

Edit 1

My problem is different from the one in How to pass `Option<&mut ...>` to multiple function calls without causing move errors?. I want to mutably dereference the reference in an Option<&mut T> multiple times, while the other one wants to pass an Option to multiple function invocations. The solutions to the other question are not applicable to my situation.

Upvotes: 8

Views: 1028

Answers (1)

Lukas Kalbertodt
Lukas Kalbertodt

Reputation: 88576

What about this?

fn f(a: Option<&mut i32>) {
    if let Some(&mut ref mut x) = a {
        *x = 6;
    }
    // ...
    if let Some(&mut ref mut x) = a {
        *x = 7;
    }
}

In this case, a doesn't need to be mutable.

The &mut ref mut feels a bit awkward, but it makes sense: first we remove a &mut by destructuring and then take a mutable reference to the value again. It's more obvious when we don't use the Option:

let mr: &mut Vec<u32> = &mut vec![];
{
    let &mut ref mut a = mr;
    a.push(3);
}
mr.push(4);

This also works. The third (special) line is equivalent to:

let a = &mut     *mr   ;
//               ^^^----- this is an lvalue of type `Vec<u32>`
//      ^^^^^^^^^^^^----- together it's of type `&mut Vec<u32>` again

In the Option case, we can't use the &mut *X version, but need to do all of it inside of the pattern. Thus the &mut ref mut x.

Upvotes: 4

Related Questions