Move from one enum item into another


#1

The following code doesn’t compile:

struct A;
struct B;

fn AtoB(a: A) -> B { B }
fn BtoA(b: B) -> A { A }

enum Enum {
    A(A),
    B(B),
}

impl Enum {
    fn swap(&mut self) {
        *self = match *self {
            Enum::A(a) => Enum::B(AtoB(a)),
            Enum::B(b) => Enum::A(BtoA(b)),
        }
    }
}

The actual A and B are of course not as trivial.

error[E0507]: cannot move out of borrowed content
  --> <anon>:14:23
   |
14 |         *self = match *self {
   |                       ^^^^^ cannot move out of borrowed content
15 |             Enum::A(a) => Enum::B(AtoB(a)),
   |                     - hint: to prevent move, use `ref a` or `ref mut a`
16 |             Enum::B(b) => Enum::A(BtoA(b)),
   |                     - ...and here (use `ref b` or `ref mut b`)

error: aborting due to previous error

Is there a way to implement this?

Here’s the playground:
https://is.gd/hu1Sds


#2

This isn’t specific to enums; you need the a and b by value to call AtoB and BtoA. There’s actually an RFC about this:

For now, you can use take_mut.


#3

To answer my own question, it’s not possible because of panics, but this works:

struct A;
struct B;

fn AtoB(a: A) -> B { B }
fn BtoA(b: B) -> A { A }

enum Enum {
    A(A),
    B(B),
    Poisoned,
}

impl Enum {
    fn swap(&mut self) {
        *self = match std::mem::replace(self, Enum::Poisoned) {
            Enum::Poisoned => panic!("Poisoned"),
            Enum::A(a) => Enum::B(AtoB(a)),
            Enum::B(b) => Enum::A(BtoA(b)),
        }
    }
}

#4

Thanks for the link.
I don’t like the “abort on panic” thing, so I think I’ll go with the Poisoned state.

It’s somewhat specific to enums: I actually want a and b move out by value because I’m changing the enum variant anyway.
Similarly it’s not possible to swap the variant type even in this simple case where there’s no conversion involved:

enum Enum {
    A1(A),
    A2(A),
}

I guess I could use some hack like transmute, but I’d like not to.