如何在不冒犯借用检查器的情况下进行计算状态转换?

时间:2017-02-16 19:10:48

标签: rust state borrow-checker

我已编写此代码用于在州之间转换,但如果未通过借阅检查程序:

struct State {
    // ...
}

impl State {
    fn next(self) -> (Self, u32) {
        // ...
    }
}

struct StateHolder {
    state: State
}

impl StateHolder {
    fn next_value(&mut self) -> u32 {
         // ERROR: Cannot move out of borrowed context
         let (new_state, byproduct) = self.state.next(); 
         self.state = new_state;
         return byproduct;
    }
}

这似乎是我通常使用std::mem::replace的情况,但由于new_state的值取决于旧状态,因此它只是在这里工作。

我知道我可以通过State实施Clone并执行此操作来完成这项工作:

fn next_value(&mut self) -> u32 {
    let (new_state, byproduct) = self.state.clone().next(); 
    self.state = new_state;
    return byproduct;
}

但我不想做那样的不必要的副本,或者需要State来实现Clone。 (我认为编译器会在这种情况下优化副本,但我不想依赖它。)

我也知道在不安全的Rust中有很多方法可以做到这一点,但对于这种简单且明显安全的模式来说,这似乎有些过分。

有没有办法在安全的Rust中写这个,没有不必要的复制?

1 个答案:

答案 0 :(得分:6)

最简单的方法:

fn next(&mut self) -> u32;

我会诚实地说,消费和归还self感觉真的很奇怪,而且这也是不切实际的,如此处所示。

如果您遇到fn next(self) -> (Self, u32),但愿意改变StateHolder的布局,请选择state: Option<State>,因为Optiontake

let (new_state, byproduct) = self.state.take().unwrap().next();
self.state = Some(new_state);
byproduct

如果您在没有Option的情况下被卡住,或者使用起来太痛苦,那么您确实可以使用std::mem::replace,但是您需要一些State来执行此操作。如果State实现Default,这很容易,但可能很昂贵:

let (new_state, byproduct) =
    std::mem::replace(&mut self.state, State::default()).next();
std::mem::replace(&mut self.state, new_state);
byproduct

最后,您确实可以打破unsafe以将值移出self.state ...但是如果计算在您填写之前发生恐慌,那么您已经打开了未定义行为的大门土地,那是baaad。