1

I would like to extract a value from a pattern matched mutable reference and discard the old one.

This is a minimal example I've come up with:

fn main() {
    #[derive(Debug)]
    enum D<A> {
        E1(A),
        E2(A, A),
    };

    trait Zero<A> {
        fn zero() -> A;
    }

    impl<A> D<A> {
        pub fn push2(&mut self, e: A) {
            match self {
                D::E1(e1) => {
                    *self = D::E2(*e1, e);
                }
                _ => unimplemented!(),
            }
        }

        pub fn push(&mut self, e: A)
        where
            A: Zero<A>,
        {
            match self {
                D::E1(e1) => {
                    let mut r = A::zero();
                    std::mem::swap(&mut r, e1);
                    *self = D::E2(e, r);
                }
                _ => unimplemented!(),
            };
        }
    }

    impl Zero<i32> for i32 {
        fn zero() -> i32 {
            0
        }
    }

    let mut d = D::E1(10);
    d.push(11);

    println!("{:?}", d);
}

playground

push2 fails with:

error[E0507]: cannot move out of borrowed content
  --> src/main.rs:17:39
   |
17 |                         *self = D::E2(*e1, e);
   |                                       ^^^ cannot move out of borrowed content

I would like to achieve this without requiring the Copy trait as I have some boxed types inside and ideally would like to remove the need for the dummy variable (A::zero()).

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
jsmithc9
  • 17
  • 4
  • I have been suggested to use std::mem::replace instead of std::mem::swap which makes things a lot less verbose but it's still an ugly way of achieving the result. – jsmithc9 Jun 02 '18 at 04:36

1 Answers1

1

If I understand your code, A will be some number because it implements Zero. The numeric values implement Copy in Rust, so you can use this to your advantage:

pub fn push(&mut self, e: A)
where
    A: Zero<A> + Copy,
{
    match self {
        D::E1(e1) => *self = D::E2(e, *e1),
        _ => unimplemented!(),
    };
}

If you do not want to bound against Copy, you can use std::mem::replace as suggested:

pub fn push(&mut self, e: A)
where
    A: Zero<A>,
{
    use std::mem::replace;
    match self {
        D::E1(e1) => *self = D::E2(e, replace(e1, A::zero())),
        _ => unimplemented!(),
    };
}

That is the idiomatic way to do so.

You need the dummy element because you cannot do this:

D::E1(e1) => *self = D::E2(e, *e1),

The reason is that the compiler evaluates the parameters first, and *e1 tries to take the ownership of borrowed data, but that is forbidden.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Boiethios
  • 38,438
  • 19
  • 134
  • 183
  • A will not necessarily be a number, it has a zero as a general element you can get for the type since for this to work it needs something to be swapped inside the structure I'm trying to get the element(s) out of. I'm looking for a solution that would not require the Copy trait and a dummy element, if it exists. – jsmithc9 Jun 02 '18 at 07:11
  • @jsmithc9 You cannot do that. I edited to explain why. – Boiethios Jun 02 '18 at 07:20
  • Thanks for elaborating. – jsmithc9 Jun 02 '18 at 16:09