我有一个类似有限状态机的枚举,它具有包含不同类型缓冲区的不同状态。
在我的“转换”方法中,我正在更新一个枚举字段,并有条件地将
self
替换为不同的变体,重用旧的字段值。
但是在一种特定情况下,我想将大缓冲区从旧变体移动到新变体,所检查的借用不允许我这样做。
在避免整个缓冲区的昂贵副本的同时执行此操作的正确方法是什么?
impl EnumBased {
fn step(&mut self) {
match self {
EnumBased::A(..) => { /* do something */ },
EnumBased::B {
buffer, // &mut Buffer
} => {
buffer.do_something()
if something {
// consume buffer, moving out of self
let inner = buffer.to_inner(); // <-- at this point, can't move out of a mutable reference
// overwrite now-defunct self
*self = EnumBased::C { inner, ... }
}
},
}
}
}
首先,我已将您的代码更改为几乎可以编译的代码。
pub enum EnumBased {
B { buffer: Vec<u8> },
C { buffer: Vec<u8> },
}
impl EnumBased {
pub fn step(&mut self) {
*self = match self {
Self::B { buffer } => {
buffer.pop();
Self::C { buffer } // expected `Vec<u8>`, found `&mut Vec<u8>`
}
_ => todo!(),
};
}
}
如果可以廉价地构建缓冲区(如
Vec
),您可以简单地将其替换为空缓冲区。一种方法是使用 std::mem::take
。
*self = match self {
Self::B { buffer } => {
buffer.pop();
let buffer = std::mem::take(buffer);
Self::C { buffer }
}
_ => todo!(),
};
如果这是不可能的,您可以使您的枚举可以廉价地构建。要么使用现有的廉价变体,要么专门为此目的制作一个新变体。然后您可以使用
std::mem::replace
来短暂替换 self
。
pub enum EnumBased {
B { buffer: Vec<u8> },
C { buffer: Vec<u8> },
Invalid
}
impl EnumBased {
pub fn step(&mut self) {
*self = match std::mem::replace(self, Self::Invalid) {
Self::B { mut buffer } => {
buffer.pop();
Self::C { buffer }
}
_ => todo!(),
};
}
}
使用专用变体的优点是,如果您在此函数之外看到它,您就知道出了问题。