Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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.

>Solution :

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>.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading