Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read reference from Option<&mut T> multiple times

Tags:

rust

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.

like image 269
Adrian Willenbücher Avatar asked Feb 18 '17 21:02

Adrian Willenbücher


1 Answers

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.

like image 141
Lukas Kalbertodt Avatar answered Nov 01 '22 01:11

Lukas Kalbertodt