rust 在传输不可复制值时实现就地枚举修改

ki0zmccv  于 2022-12-19  发布在  其他
关注(0)|答案(4)|浏览(102)

有没有一种方法可以让merge函数使用&mut self,消耗内部枚举值,并在合并时将其推到一个新的Vector上?我一直在与编译器争论这个问题a- PEBKAC,但在哪里?
如果这不可能,可以通过在Val上实现Clone trait来解决吗?(但不能通过Copy trait!)

struct Val();

enum Foo {
    One(Val),
    Many(Vec<Val>),
}

impl Foo {
    pub fn merge(&mut self, other: Self) {
        match (*self, other) {
//             ^^^^^
// move occurs because `*self` has type `Foo`, which does not implement the `Copy` trait
// cannot move out of `*self` which is behind a mutable reference
//

            (Self::One(a), Self::One(b)) => *self = Self::Many(vec![a, b]),
            (Self::One(a), Self::Many(mut b)) => {
                b.insert(0, a);
                *self = Self::Many(b)
            }
            (Self::Many(mut a), Self::One(b)) => {
                a.push(b);
            }
            (Self::Many(mut a), Self::Many(b)) => {
                a.extend(b);
            }
        };
    }
}
pu3pd22g

pu3pd22g1#

我们不能移出self,但可以同时用一个空的虚拟对象替换它:

impl Foo {
    pub fn merge(&mut self, other: Self) {
        let mut tmp = Self::Many(vec![]);
        std::mem::swap(&mut tmp, self);
        *self = match (tmp, other) {
            (Self::One(a), Self::One(b)) => Self::Many(vec![a, b]),
            (Self::One(a), Self::Many(mut b)) => {
                b.insert(0, a);
                Self::Many(b)
            }
            (Self::Many(mut a), Self::One(b)) => {
                a.push(b);
                Self::Many(a)
            }
            (Self::Many(mut a), Self::Many(b)) => {
                a.extend(b);
                Self::Many(a)
            }
        };
    }
}
8aqjt8rx

8aqjt8rx2#

这里比较棘手的是One变量要求在任何时候都有一个值存在,你不能把它的T取出来,即使是暂时的也不行,因为你必须在那里放一些别的东西来代替它,如果它是One(Option<T>),我们可以放一个None进去,但是对于One(T),我们不能这么做。
你可以做的是用一个空的Many临时替换*self,然后生成替换对象并覆盖空对象,这样调用者就看不到它了。

impl Foo {
    pub fn merge(&mut self, other: Self) {
        let this = std::mem::replace(self, Self::Many(vec![]));
        *self = match (this, other) {
            (Self::One(a), Self::One(b)) => Self::Many(vec![a, b]),
            (Self::One(a), Self::Many(mut b)) => {
                b.insert(0, a);
                Self::Many(b)
            }
            (Self::Many(mut a), Self::One(b)) => {
                a.push(b);
                Self::Many(a)
            }
            (Self::Many(mut a), Self::Many(b)) => {
                a.extend(b);
                Self::Many(a)
            }
        };
    }
}
1hdlvixo

1hdlvixo3#

为了完整起见,我想再展示一种方法:

pub fn merge(&mut self, other: Self) {
    let self_vec = match self {
        Self::One(_) => {
            let this = std::mem::replace(self, Self::Many(vec![]));
            match (self, this) {
                (Self::Many(v), Self::One(o)) => {
                    v.push(o);
                    v
                }
                _ => unreachable!(),
            }
        }
        Self::Many(v) => v,
    };
    match other {
        Self::One(a) => self_vec.push(a),
        Self::Many(a) => self_vec.extend(a),
    };
}

(在分配失败的情况下,可能会有一些小的行为差异,但我怀疑是否有人会在意。)

l7wslrjt

l7wslrjt4#

为了完整起见,另一个避免临时值的解决方案是:

impl Foo {
    pub fn merge(self, other: Self) -> Self {
      match (self, other) {
        (Self::One(a), Self::One(b)) => Self::Many(vec![a, b]),
        (Self::One(a), Self::Many(mut b)) => {
          b.insert(0, a);
          Self::Many(b)
        },
        (Self::Many(mut a), Self::One(b)) => {
          a.insert(0, b);
          Self::Many(a)
        },
        (Self::Many(mut a), Self::Many(b)) => {
          a.extend(b);
          Self::Many(a)
        }
      }
      
    }
}

这将消耗self并返回一个新的Foo,因此您可以这样调用它:

foo = foo.merge(other_foo);

相关问题