我有一个代表状态机的 Rust 枚举。我需要在状态之间移动一些数据(其中数据未实现 Copy
)。什么是好的使用方法?
基本上,我想消除这段代码中对 bravo.clone()
的调用。当原始数据将被删除时,不得不克隆该数据是令人失望的。我宁愿做的是 bravo: *bravo
— 将 bravo
的旧值移出 State1
并移入状态 2
。但我不能直接这样做,因为这会在构造 State2
时短暂地使 self.state
的值无效。
enum MyStateMachine {
Idle,
State1 {
alpha: usize,
bravo: String,
},
// State2 is a superset of State1
State2 {
alpha: usize,
bravo: String,
charlie: usize,
},
}
impl MyStateMachine {
fn to_state2(&mut self, charlie: usize) -> Result<(), String> {
use MyStateMachine::*;
match self {
State1 { alpha, bravo } => {
*self = State2 {
// Copy type moves between states OK
alpha: *alpha,
// Non-copy types require a call to .clone()
bravo: bravo.clone(),
charlie,
};
Ok(())
}
_ => Err("Must be in State1".into())
}
}
}
最佳答案
你不能直接这样做,因为 Rust 确保 *self
必须每次都有效。这很好,因为如果你的程序在某处崩溃并且它必须调用 drop()
会发生什么?和你的 *self
不一致?
幸运的是你的对象有一个方便的 Idle
可以用作中间值的状态。最后的技巧在 std::mem::replace()
中:
impl MyStateMachine {
fn to_state2(&mut self, charlie: usize) -> Result<(), String> {
use MyStateMachine::*;
// take ownership of the old status, installing the dummy Idle
let old = std::mem::replace(self, Idle);
match old {
State1 { alpha, bravo } => {
//assign the final status
*self = State2 {
alpha: alpha,
// no clone!
bravo: bravo,
charlie,
};
Ok(())
}
_ => {
// restore old status before returning error
std::mem::replace(self, old);
Err("Must be in State1".into())
}
}
}
}
如果您没有 Idle
还有其他解决方法。例如,您可以移出 bravo
来自 self
如果它的类型有这样的值,则用虚拟值替换它,然后轻松构建新状态。也许是这样的:
impl MyStateMachine {
fn to_state2(&mut self, charlie: usize) -> Result<(), String> {
use MyStateMachine::*;
*self = match self {
State1 { alpha, bravo } => {
//steal the value replacing it with a dummy
//remember that String::new() does not allocate memory
let bravo = std::mem::replace(bravo, String::new());
State2 {
alpha: *alpha,
// no clone!
bravo,
charlie,
}
}
_ => return Err("Must be in State1".into())
};
Ok(())
}
}
如果您的 brave
类型没有合适的虚拟值,您也可以将其类型替换为 Option<_>
并使用 Option::take()
而不是 mem::replace()
.
关于rust - 如何在枚举项之间移动非复制数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57614924/