bytesized
bytesized

Reputation: 1522

Given an optional mutable reference, can I pass it to another function without moving it?

I am working on a little Rust project where many functions take an optional mutable reference to a struct. For simplicity, let's say that this struct is a String. So the functions look something like this:

fn append_to_string(maybe_string: Option<&mut String>) {
    if let Some(s) = maybe_string {
        s.push('1');
    }
}

My main function has ownership of the optional structure. So it can easily call these functions using Option::as_mut:

fn main() {
    let mut maybe_string = Some(String::new());
    append_to_string(maybe_string.as_mut());
    println!("{:?}", maybe_string);
}

This all seems to work fine. But the problem comes when one of the functions needs to call others.

fn append_multiple_to_string(maybe_string: Option<&mut String>) {
    for _ in 0..2 {
        append_to_string(maybe_string);
    }
}

I can't compile this, because append_multiple_to_string moves maybe_string into append_to_string in the first iteration of the loop, so it can't use it again in subsequent iterations of the loop. See this Rust Playground.

I've actually figured out a way to make this work by extracting the reference from the Option and constructing a new Option for each iteration, like this (Rust Playground):

fn append_multiple_to_string(maybe_string: Option<&mut String>) {
    match maybe_string {
        Some(string) => {
            for _ in 0..2 {
                append_to_string(Some(string));
            }
        }
        None => {
            for _ in 0..2 {
                append_to_string(None);
            }
        }
    }
}

But this feels very cumbersome and I don't love that I have to repeat basically the same code twice to make it work. I feel like I must be missing a more elegant way of doing this, but I just don't seem to be able to figure out what it is. I guess I could make a macro that could take one copy of the code and expand it, but I have written macros before and I find them to be difficult to write and maintain, so I'd rather avoid that.

I assume that there is no way to make a copy of the Option to pass in, because then I would have two simultaneous mutable references to the same data. So am I just stuck with the ugly code that I have?

I'm open to changing the argument type away from Option<&mut String>, but I'm not sure what to change it to so that I can avoid this problem. If I do need to change it, I would prefer not to change it in such a way that the functions can change the value of main's maybe_string.is_some(). That is to say, with my current code, if a function calls maybe_string.take(), it is only taking the value out of its copy of the Option, not main's copy.

I would also prefer not to solve this problem using unsafe code.

Upvotes: 2

Views: 419

Answers (1)

Freyja
Freyja

Reputation: 40934

You can use Option::as_deref_mut:

fn append_multiple_to_string(mut maybe_string: Option<&mut String>) {
//                           ^^^
    for _ in 0..2 {
        append_to_string(maybe_string.as_deref_mut());
//                                   ^^^^^^^^^^^^^^^
    }
}

as_deref_mut will turn a &'a mut Option<P> (where P is a mutable pointer type implementing DerefMut) into an Option<&'a mut P::Target>. In the case of a &mut T pointer, this means it will just turn a &'a mut Option<&'b mut T> into an Option<&'a mut T>, but it also works with other mutable pointer types, such as &'a mut Option<Box<T>>Option<&'a mut T> or &'a mut Option<String>Option<&'a mut str>.

Upvotes: 3

Related Questions