Is there any option to extract a mutable reference out of an Option<&mut Foo>?
All I found was as_ref() which extracts an immutable reference.
Is there any option to extract a mutable reference outside of a
Option<&mut Foo>.
Yes, but it requires a bit of effort to prevent consuming the Option, since &mut T is not Copy.
Let's assume you have an Option<&mut Foo> which is Some. (The same applies for a non-Some option, just replace unwrap() with appropriate matching.) For example:
let mut foo = Foo;
let mut opt = Some(&mut foo);
opt.unwrap() will give you &mut Foo, but it will move it out of the Option, because &mut Foo is not Copy:
let mut_ref = opt.unwrap();
drop(mut_ref); // done with mut_ref
println!("{:?}", opt); // XXX doesn't compile, opt is consumed
That will leave the Option unusable even after mut_ref is out of scope. We don't want that, we want to borrow the inside of the option and retain the use of the Option once that borrow is dropped. For that we can use Option::as_mut:
let mut_ref_ref = opt.as_mut().unwrap();
drop(mut_ref_ref);
println!("{:?}", opt); // compiles
There are two things to note here: first, you need to use as_mut(), not as_ref() as you attempted, because as_ref() would give you a mutable reference behind a shared reference, which renders it useless. Second, unwrapping as_mut() returns an &mut &mut Foo, not the &mut Foo we wanted. It's very close, though - for example, auto-dereferencing allows you to call a function that accepts &mut Foo:
fn wants_ref(_r: &mut Foo) {}
let mut_ref_ref = opt.as_mut().unwrap(); // got &mut &mut Foo
wants_ref(mut_ref_ref); // but we can send it to fn expecting &mut Foo
If you for some reason wanted an actual &mut Foo, one would think you'd get it by dereferencing the &mut &mut Foo. However, this doesn't work:
// XXX doesn't compile
let mut_ref = *opt.as_mut().unwrap();
That doesn't compile because * tries to dereference &mut &mut Foo and extract the underlying &mut Foo. But &mut Foo is not Copy, so that's attempting to "move out of a mutable reference", which rustc will tell you cannot be done. However, in this case it can, you need to perform an explicit reborrow, i.e. turn EXPR to &mut *EXPR, where EXPR evaluates to a mutable reference. &mut *EXPR will create a new mutable reference to the same data. It doesn't count as aliasing because the old mutable reference will be (statically unusable) until the new one is dropped. It's basically the same mechanism that allows projection to work on mutable references (i.e. let x_ref = &mut point.x, with point being an &mut Point) - but applied to the object itself.
With explicit reborrow it looks like this:
// `&mut *` is reborrow, and `*` dereferences `&mut &mut Foo`
let mut_ref = &mut **opt.as_mut().unwrap();
wants_ref(mut_ref);
println!("{:?}", opt); // opt is still usable
Playground
The correct way of doing that is through the as_mut() method.
One tricky bit is that you need to satisfy the &mut self parameter type. The key observation is that if you own a Option<&mut Foo> value, you can safely turn into a mut version of the same (as you are the only owner of it).
That is, you can freely go from a Option<&mut Foo> (owned, immutable) to a mut Option<&mut Foo> (owned, immutable) and call the .as_mut() on that to reach the &mut Foo (reference, mutable).
So overall it looks like this:
let maybe_foo: Option<&mut Foo> = Some(&mut foo);
let mut maybe_foo = maybe_foo;
if let Some(mutable_reference_to_foo) = maybe_foo.as_mut() {
...
}
Playground
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With