ideasman42
ideasman42

Reputation: 47978

How to pass `Option<&mut ...>` to multiple function calls without causing move errors?

Since it's possible to pass a mutable reference to a vector around (without causing moves), how can an Option<reference> be passed to functions multiple times without causing borrow checking errors?

This simple example just shows what happens when an Option<&mut Vec<usize>> is passed multiple times to a function:

fn maybe_push(mut v_option: Option<&mut Vec<usize>>) -> usize {
    let mut c = 0;
    if let Some(ref mut v) = v_option.as_mut() {
        for i in 0..10 {
            v.push(i);
            c += i;
        }
    }

    return c;
}

fn maybe_push_multi(v_option: Option<&mut Vec<usize>>) -> usize {
    let mut c = 0;
    c += maybe_push(v_option);
    c += maybe_push(v_option);
    c += maybe_push(None);
    return c;
}

fn main() {
    let mut v: Vec<usize> = vec![];
    let v_option = Some(&mut v);
    println!("{}", maybe_push_multi(v_option));
}

(Playground)

Gives the error:

error[E0382]: use of moved value: `v_option`
  --> <anon>:17:21
   |
16 |     c += maybe_push(v_option);
   |                     -------- value moved here
17 |     c += maybe_push(v_option);
   |                     ^^^^^^^^ value used here after move
   |
   = note: move occurs because `v_option` has type `std::option::Option<&mut std::vec::Vec<usize>>`, which does not implement the `Copy` trait

Upvotes: 2

Views: 1330

Answers (3)

maoe
maoe

Reputation: 41

As of 1.40.0, you can use Option::as_deref_mut.

fn maybe_push_twice(mut v_option: Option<&mut Vec<usize>>) -> usize {
    let mut c = 0;
    c += maybe_push(v_option.as_deref_mut());
    c += maybe_push(v_option);
    c
}

Here is a playground modified from the original playground.

Upvotes: 2

aSpex
aSpex

Reputation: 5216

You can destructure the Option with a match expression and then create a new Option value for every call of the function maybe_push():

fn maybe_push_twice(v_option: Option<&mut Vec<usize>>) -> usize {
    let mut c = 0;
    match v_option {
        Some(v) => {
            c += maybe_push(Some(v));
            c += maybe_push(Some(v));
        }
        None => {
            c += maybe_push(None);
            c += maybe_push(None);        
        }
    };
    return c;
}

Here is a more convenient way:

fn maybe_push_twice(mut v_option: Option<&mut Vec<usize>>) -> usize {
    let mut c = 0;
    c += maybe_push(v_option.as_mut().map(|x| &mut **x));
    c += maybe_push(v_option);
    return c;
}

You can use a trait instead of a macro:

trait RefMut<T> {
    fn ref_mut(&mut self) -> Option<&mut T>;
}

impl<'t, T> RefMut<T> for Option<&'t mut T>{
    #[inline]
    fn ref_mut(&mut self) -> Option<&mut T>{
        self.as_mut().map(|x| &mut**x)
    }
}

fn maybe_push_twice(mut v_option: Option<&mut Vec<usize>>) -> usize {
    let mut c = 0;
    c += maybe_push(v_option.ref_mut());
    c += maybe_push(v_option);
    return c;
}

Upvotes: 2

Chris Emerson
Chris Emerson

Reputation: 14041

You can pass the Option by reference too, if you don't want it moved into the function.

fn maybe_push(mut v_option: &mut Option<&mut Vec<usize>>) -> usize

// ...

maybe_push_twice(&mut v_option);

Then replace:

maybe_push(None);

With:

maybe_push(&mut None);

Upvotes: 5

Related Questions