I want to update an enum variant while moving a field of the old variant to the new one without any cloning:
enum X {
X1(String),
X2(String),
}
fn increment_x(x: &mut X) {
*x = match *x {
X::X1(s) => X::X2(s),
X::X2(s) => X::X1(s),
}
}
This doesn't work because we can't move s
from &mut X
:
error[E0507]: cannot move out of borrowed content
--> src/lib.rs:7:16
|
7 | *x = match *x {
| ^^
| |
| cannot move out of borrowed content
| help: consider removing the `*`: `x`
8 | X::X1(s) => X::X2(s),
| - data moved here
9 | X::X2(s) => X::X1(s),
| - ...and here
Please don't suggest things like implementing an enum X { X1, X2 }
and using struct S { variant: X, str: String }
etc. This is a simplified example, imagine having lots of other fields in variants, and wanting to move one field from one variant to another.
This doesn't work because we can't move
s
from&mut X
.
Then don't do that... take the struct by value and return a new one:
enum X {
X1(String),
X2(String),
}
fn increment_x(x: X) -> X {
match x {
X::X1(s) => X::X2(s),
X::X2(s) => X::X1(s),
}
}
Ultimately, the compiler is protecting you because if you could move the string out of the enumeration, then it would be in some half-constructed state. Who would be responsible for freeing the string if the function were to panic at that exact moment? Should it free the string in the enum or the string in the local variable? It can't be both as a double-free is a memory-safety issue.
If you had to implement it on a mutable reference, you could store a dummy value in there temporarily:
use std::mem;
fn increment_x_inline(x: &mut X) {
let old = mem::replace(x, X::X1(String::new()));
*x = increment_x(old);
}
Creating an empty String
isn't too bad (it's just a few pointers, no heap allocation), but it's not always possible. In that case, you can use Option
:
fn increment_x_inline(x: &mut Option<X>) {
let old = x.take();
*x = old.map(increment_x);
}
See also:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With