Handling optional mutable reference without taking it?

I was trying to write something like the following:

fn bar(ret: Option<&mut i32>) {...}
fn foo(ret: Option<&mut i32>) {
  bar(ret);
  bar(ret); // error: `ret` is moved
}

this link suggests to use ret.as_deref_mut(), but the fact that I need to declare the option as mut bugs me.

So I went a little further and found this.

Taking the example from the link:

struct Foo {
    value: Vec<i32>,
}

let a: Option<&mut Foo> = ...;
if let Some(&mut ref mut x) = a { // `a` is not moved!
    x.value.push(0);
}

if let Some(&mut ref mut x) = a {
    x.value.push(0);
}

Now I'm a little confused since it seems that the code effectively mutates a without taking ownership of it, even though it is not declared mut!
Isn't it a violation of Rust's principle??

If it is a valid code, is there a way to simplify it without using if let?
I tried the following but map takes the ownership of Option so a cannot be used again.

let a: Option<&mut Foo> = ...;
a.map(|&mut ref mut x| x.value.push(0));

You need to reborrow the reference within an Option. You can do it like this:

fn bar(ret: Option<&mut i32>) { }
fn foo(mut ret: Option<&mut i32>) {
  bar(ret.as_mut().map(|x|&mut **x));
  bar(ret.as_mut().map(|x|&mut **x));
}

Maybe someone could find nicer solution.

fn bar(ret: Option<&mut i32>) {...}

This function signature moves the Option into bar. (C++ calls this "pass by value", but Rust has a much stronger guarantee: After moving a value, it can no longer be used.)

Consider this slightly modified example: Rust Playground

The second call to bar is not necessary to get the same error! any use of ret after it has been moved to bar will cause it.

If you don't control the bar API that you are interacting with, you can use something like the approach described in the comment above. But if bar is your own function, you might want to loan the option instead:

fn bar(ret: &mut Option<&mut i32>) {...}
fn foo(ret: &mut Option<&mut i32>) {
  //        ^^^^ These are new!
  bar(ret);
  bar(ret); // Works just fine
}
1 Like

You're performing a reborrow with pattern syntax to conditionally access the wrapped value. A reborrow as in

let x = &mut *inner_a;

The real principle is exclusivity, and reborrows preserve that - you can't use a (or the &mut it contains) while the reborrow is active.

You don't have to declare a variable of type &mut as a mut binding to mutate what it points to, and this is an extension of that. Note that mut bindings are basically a lint so you don't overwrite the variable or take a &mut to it unintentionally (whereas & T and &mut T are different types with very different semantics).

2 Likes

Thanks for your answer.
Let me summarize what I have understood. Please correct me if I'm wrong.

Since there is a way to reborrow the inner mutable reference without taking or requiring mut Option using pattern matching trick, I can achieve my original purpose like this:

fn bar(ret: Option<&mut i32>) {...}

fn foo(ret: Option<&mut i32>) {
    bar(match ret {
        Some(&mut ref mut x) => Some(x),
        None => None,
    });

    bar(match ret {
        Some(&mut ref mut x) => Some(x),
        None => None,
    });
}

I was wondering if I could somehow write it like this, without mut Option:

fn foo(ret: Option<&mut i32>) {
   bar(ret.reborrow_inner());
   bar(ret.reborrow_inner());
   // ret.as_deref_mut() does exactly this, but it requires `mut ret`
}

If &mut T does not require mut binding to mutate what it points to, I was guessing there must be a cleaner way to achieve this without mut Option.

However, any method call to achieve this would require taking ownership or mutable reference of Option, there seems no way to achieve what I want. The only way is to use that pattern matching trick.

Some failed trials:

    bar(match ret { // `ret` is moved so can't be used again
        Some(x) => Some(&mut *x),
        None => None,
    });

    bar(match *ret { // `Option` cannot be derefenced
        Some(x) => Some(&mut *x),
        None => None,
    });

    bar(match ret.as_deref_mut() { // requires `mut ret`
        Some(x) => Some(x),
        None => None,
    });

It's interesting that we can reborrow Option's inner mutable reference without taking or requiring mut Option with pattern matching.
It's interesting that there are something that can be done with pattern matching that can't be done otherwise.
Maybe it's the limitation of method calls..

Yeah, patterns let you define a sort of road map deep into a data structure to some place (here, *Option::Some.0, to abuse some notation) and then move or copy just that place... or, take a reference to it. There's no calling convention that does the same thing, so instead we pass by reference; if we need a mutable reference, we pass a &mut _.

The language would have to be extended to support something like this in a function call.

Most people would just give the ret a mut binding.

You could make a macro instead.

1 Like

Yes, I would do that, too.
It was just for the sake of curiosity :slight_smile:
Thanks for the clarification !

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.