Reputation: 1522
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
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