Extract from a &mut

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.