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