我有一个代表状态机的 Rust 枚举。我需要在状态之间移动一些数据(其中数据未实现Copy
)。有什么好的使用方法?
基本上,我想消除bravo.clone()
此代码中的调用。当原始数据将被删除时,必须克隆该数据是令人失望的。我宁愿做的事情是沿着bravo: *bravo
\xe2\x80\x94 的路线将旧值移出bravo
和State1
移入State2
. 但我不能直接这样做,因为这会暂时使self.state
while Construction的值无效State2
。
enum MyStateMachine {\n Idle,\n State1 {\n alpha: usize,\n bravo: String,\n },\n // State2 is a superset of State1\n State2 {\n alpha: usize,\n bravo: String,\n charlie: usize,\n },\n}\n\nimpl MyStateMachine {\n fn to_state2(&mut self, charlie: usize) -> Result<(), String> {\n use MyStateMachine::*;\n\n match self {\n State1 { alpha, bravo } => {\n *self = State2 {\n // Copy type moves between states OK\n alpha: *alpha, \n // Non-copy types require a call to .clone()\n bravo: bravo.clone(),\n charlie,\n };\n Ok(())\n }\n _ => Err("Must be in State1".into())\n }\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n
你不能直接这样做,因为 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())
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果您没有,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(())
}
}
Run Code Online (Sandbox Code Playgroud)
如果您的类型brave
没有合适的虚拟值,您也可以将其类型替换为 anOption<_>
并使用Option::take()
代替mem::replace()
。