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

Oth*_*ers 3 state rust 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这似乎是我经常使用的那种情况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;
}
Run Code Online (Sandbox Code Playgroud)

但我宁愿不做那样不必要的复制,也不要求State实施Clone.(我认为编译器会在这种情况下优化副本,但我不想依赖它.)

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

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

Mat*_* M. 6

最简单的方法:

fn next(&mut self) -> u32;
Run Code Online (Sandbox Code Playgroud)

我会诚实地说,消费和退货都感觉很奇怪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
Run Code Online (Sandbox Code Playgroud)

如果你没有被困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
Run Code Online (Sandbox Code Playgroud)

最后,你确实可以打破unsafe将价值移出self.state......然而,如果计算在你填补它之前就会发生恐慌,那么你就打开了未定义行为之地的大门,这就是baaad.