9
votes

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.

1
The other answers are just fine. Really. Just take a mutable reference to your argument as the first thing if you need to.Shepmaster
You mean like this: let tmp = &mut a; if let &mut Some(ref mut x) = tmp { ...? But then a has to be mut, which is exactly what I'd like to avoid.Adrian Willenbücher
But then a has to be mut; yes, what's the problem with that? No one outside of your function cares (or can care) if that argument is mut. You own it so you can do whatever you want. You could also do let mut tmp1 = a; let mut tmp2 = &mut tmp1; if it really bothered you.Shepmaster
"yes, what's the problem with that?" I was looking for a solution that is better/cleaner/more elegant than the one I had already found (the one below "This would work").Adrian Willenbücher

1 Answers

4
votes

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.