当旧的枚举被丢弃时,有没有一种方法可以从 &mut 枚举中提取值?

Is there a way of extracting a value from a &mut enum when the old one is discarded?

我想从模式匹配的可变引用中提取一个值并丢弃旧的。

这是我想出的一个最小示例:

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 失败:

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

我想在不需要 Copy 特性的情况下实现这一点,因为我内部有一些盒装类型,理想情况下我想消除对虚拟变量 (A::zero()) 的需要。

如果我理解你的代码,A 将是一些数字,因为它实现了 Zero。数值在 Rust 中实现 Copy,所以你可以利用它来发挥你的优势:

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

如果你不想绑定Copy,你可以按照建议使用std::mem::replace

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!(),
    };
}

这是惯用的方法。

需要虚拟元素,因为您不能这样做:

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

原因是编译器首先评估参数,*e1试图取得借用数据的所有权,但这是被禁止的。