How to cleanly use Option<&mut> multiple times?

I have a function test1 that takes an Option<&mut> param. The idea is to then pass that param onto other function calls test2 to collect data.

The problem is that the first calls of test2 transfers ownership and so the second call won't compile.

I understand how this can be a problem with a generic type, but since the Option is holding a &mut I think there must be a clean way to do this?

The best I can manage is:

fn test1(param: Option<&mut usize>) {
    if let Some(p) = param {
        test2(Some(p));
        test2(Some(p));
    } else {
        test2(None);
        test2(None);
    }
}

But that's a mess of duplication... must be a better way?

Here's setup I'm working with:

fn main() {
    let mut i = 0;
    let o_i = Some(&mut i);
    test1(o_i);
    println!("{}", i);
}

fn test1(param: Option<&mut usize>) {
    test2(param);
    test2(param);
}

fn test2(param: Option<&mut usize>) {
    if let Some(i) = param {
        *i += 1;
    }
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0382]: use of moved value: `param`
  --> src/main.rs:10:11
   |
8  | fn test1(param: Option<&mut usize>) {
   |          ----- move occurs because `param` has type `std::option::Option<&mut usize>`, which does not implement the `Copy` trait
9  |     test2(param);
   |           ----- value moved here
10 |     test2(param);
   |           ^^^^^ value used here after move

error: aborting due to previous error

For more information about this error, try `rustc --explain E0382`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

You could introduce a function like this:

fn brw_opt<'a, T>(opt: &'a mut Option<&mut T>) -> Option<&'a mut T> {
    match opt {
        Some(r) => Some(&mut *r),
        None => None,
    }
}
fn test1(mut param: Option<&mut usize>) {
    test2(brw_opt(&mut param));
    test2(param);
}

Thank you, that does the trick, though I'm going to have to puzzle over the function a bit longer before I understand what's gong on. Anyway thanks again for your help!

Basically it works for the same reason this works:

fn test1(param: Option<&mut usize>) {
    if let Some(p) = param {
        test2(Some(p));
        test2(Some(p));
    } else {
        test2(None);
        test2(None);
    }
}

It creates a sub-borrow of the mutable reference and puts that borrow in a new option.

You can also use Option::as_deref_mut, the std version of @alice's brw_opt

fn test1(mut param: Option<&mut usize>) {
    test2(param.as_deref_mut());
    test2(param);
}

Ah I was looking for a combinator that could do it, but it didn't come across me that this would work.

Thanks, that's a little easier. I did browse through the method originally, but couldn't quite make all the template transformations in my head to see that it was the solution.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.