如何在枚举项之间移动非复制数据

Eva*_*van 2 ownership rust

我有一个代表状态机的 Rust 枚举。我需要在状态之间移动一些数据(其中数据未实现Copy)。有什么好的使用方法?

\n\n

基本上,我想消除bravo.clone()此代码中的调用。当原始数据将被删除时,必须克隆该数据是令人失望的。我宁愿做的事情是沿着bravo: *bravo\xe2\x80\x94 的路线将旧值移出bravoState1移入State2. 但我不能直接这样做,因为这会暂时使self.statewhile Construction的值无效State2

\n\n
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

rod*_*igo 7

你不能直接这样做,因为 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还有其他解决方法。例如,如果其类型存在这样的值,则可以将其替换为虚拟值,然后轻松构建新状态bravoself也许是这样的:

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