Is it possible to switch variants in the value of a mutable reference (&mut E<T>
) without additional constraints on T
, and without resorting to unsafe code?
That is, given an enum:
enum E<T> {
VariantA(T),
VariantB(T)
}
What is the correct way of writing this:
let x: E<???> = E::VariantA(??);
change_to_variant_b(&mut x);
assert_eq!(x, E::VariantB(??));
As an alternative for this particular case, you could consider changing to a struct with a
T
and a plain enum: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:
The problem is once you've moved the value from
self
intoval
, what should happen to the memory representingT
insideself
?If we copied the bits and then a panic occurred in "Things happen", the destructor for both
val
and theT
insideself
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
orDefault
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 (usingDefault
) or duplicate the entire original value (usingClone
).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.I am going to go on a limb here and say No.
It is possible with just a minor change to the signature though:
It is possible using
unsafe
:It is possible with additional bounds (
Default
, orClone
):