is it possible to switch variants in the value of a mutable reference
As Matthieu M. said, the general answer is "no". The reason is that doing so would leave the enum in an indeterminate state, which would allow accessing undefined memory, which would allow breaking Rust's safety guarantees. As an example, let's pretend that this code compiled without error:
impl<T> E<T> {
fn promote_to_b(&mut self) {
if let E::VariantA(val) = *self {
// Things happen
*self = E::VariantB(val);
}
}
}
The problem is once you've moved the value from self
into val
, what should happen to the memory representing T
inside self
?
If we copied the bits and then a panic occurred in "Things happen", the destructor for both val
and the T
inside self
would run, but since these point at the same data, this would lead to a double free.
If we didn't copy the bits, then you couldn't safely access the val
inside "Things happen", which would be of marginal usefulness.
The by-value solution works because the compiler can track who is supposed to call the destructor: the function itself. Once you are inside the function, the compiler knows which specific lines might need to free the value and properly calls them in case of panic.
The Clone
or Default
solution works because you never move the value out of the original enum. Instead, you can replace the original enum with a dummy value and take ownership of the original (using Default
) or duplicate the entire original value (using Clone
).
The replace_with
RFC (#1736) proposed to add a method that would allow this to work while ensuring that proper memory semantics were upheld, but that RFC was not accepted.