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

How can I do computed state transitions without offending the 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 中编写它,而无需进行不必要的复制?

最简单的方法:

fn next(&mut self) -> u32;

老实说,消费和 return self 感觉真的很奇怪,而且它也不切实际,如此处所示。


如果您坚持使用 fn next(self) -> (Self, u32) 但愿意更改 StateHolder 的布局,请使用 state: Option<State>,因为 Option 具有 take

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 中移出...但是,如果计算在您重新填充之前出现恐慌,那么您已经打开了大门未定义的行为土地,那是糟糕的。