pub enum Foo {
A{ a: A },
B{ a: A, b: B}
}
impl Foo {
fn extract_b(&mut self) -> Option<B> {
match self {
Foo::A { .. } => None,
Foo::B { .. } => {
// can we change *self to a Foo::A
// and extract a B from this ?
}
}
}
}
If type B
implements Default
, you can call std::mem::take() on it.
Otherwise you could make the type of field b
Option<B>
rather than B
, and call b_opt.take() on that.
You can do it unsafely.
impl Foo {
fn extract_b(&mut self) -> Option<B> {
match self {
Foo::A { .. } => None,
Foo::B { a, b } => unsafe {
// Safety: No destructors run here, so all three pointer
// operations will succeed, and therefore no value is
// duplicated.
let a_val = std::ptr::read(a);
let b_val = std::ptr::read(b);
std::ptr::write(self, Foo::A { a: a_val });
Some(b_val)
}
}
}
}
5 Likes
You can use the take_mut
crate for this, which uses unsafe
code to achieve it.
I wish this would just work:
impl Foo {
fn extract_b(&mut self) -> Option<B> {
match self {
Foo::A { .. } => None,
&mut Foo::B { a, b } => {
*self = Foo::A { a };
Some(b)
}
}
}
}
The reason it doesn't work is because of panic unwinding. If panic always aborted, the language could support moving out and back in to a place behind a mut reference, which to me would make sense.
1 Like
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.