在传递不可复制值的同时实现就地枚举修改

  • 本文关键字:实现 修改 枚举 可复制 rust
  • 更新时间 :
  • 英文 :


是否有一种方法可以使merge函数使用&mut self,消耗内部枚举值并在合并时将其推到新向量上?我一直在与编译器抗争,PEBKAC,但在哪里?

如果这是不可能的,它可以固定实现CloneVal特性?(但不是Copytrait!)

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);
}
};
}
}

棘手的是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)
}
};
}
}

我们不能移出self,但我们可以在此期间用一个空dummy替换它:

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)
}
};
}
}

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

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

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

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

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);

最新更新