如何在枚举项之间移动非复制数据
How to move non-Copy data between enum items
我有一个代表状态机的 Rust 枚举。我需要在状态之间移动一些数据(数据未实现 Copy
)。什么是好的使用方法?
基本上,我想消除这段代码中对 bravo.clone()
的调用。当原始数据将被删除时,不得不克隆该数据是令人失望的。我宁愿做的是按照 bravo: *bravo
的思路——将 bravo
的旧值从 State1
移到 State2
中。但我不能直接这样做,因为这会在构造 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()
.
我最终使用了 的变体。我不愿意在 match
语句之前使用 mem::replace()
,因为这会在验证可以更改状态之前推进状态。因此,我只在非复制类型上使用 mem::replace()
。
impl MyStateMachine {
fn to_state2(&mut self, charlie: usize) -> Result<(), String> {
use MyStateMachine::*;
match self {
State1 { alpha, bravo: old_bravo } => {
// Use mem::replace on non-Copy types
let bravo = std::mem::replace(old_bravo, Default::default());
*self = State2 {
// Copy types can use a simple dereference
alpha: *alpha,
bravo,
charlie,
};
Ok(())
}
_ => return Err("Must be in State1".into())
}
}
}
我有一个代表状态机的 Rust 枚举。我需要在状态之间移动一些数据(数据未实现 Copy
)。什么是好的使用方法?
基本上,我想消除这段代码中对 bravo.clone()
的调用。当原始数据将被删除时,不得不克隆该数据是令人失望的。我宁愿做的是按照 bravo: *bravo
的思路——将 bravo
的旧值从 State1
移到 State2
中。但我不能直接这样做,因为这会在构造 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()
.
我最终使用了 match
语句之前使用 mem::replace()
,因为这会在验证可以更改状态之前推进状态。因此,我只在非复制类型上使用 mem::replace()
。
impl MyStateMachine {
fn to_state2(&mut self, charlie: usize) -> Result<(), String> {
use MyStateMachine::*;
match self {
State1 { alpha, bravo: old_bravo } => {
// Use mem::replace on non-Copy types
let bravo = std::mem::replace(old_bravo, Default::default());
*self = State2 {
// Copy types can use a simple dereference
alpha: *alpha,
bravo,
charlie,
};
Ok(())
}
_ => return Err("Must be in State1".into())
}
}
}