Как я могу сделать вычисляемые переходы состояний, не оскорбляя средство проверки заимствования?


Я написал этот код для перехода между состояниями, но если не удается пройти проверку заимствования:

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. (Я думаю, что компилятор будет оптимизируйте копию в этом случае, но я не хочу полагаться на это.) Я также знаю довольно много способов сделать это в небезопасной ржавчине,но это кажется чрезмерным для такой простой и очевидно безопасной модели.

Есть ли способ сделать это в безопасной ржавчине, без ненужного копирования?

1 3

1 ответ:

Самый простой способ:

fn next(&mut self) -> u32;
Я буду честен и скажу, что это действительно странно-потреблять и возвращать 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... однако если вычисление когда-нибудь запаникует, прежде чем вы сможете заполнить его обратно, то вы открыли дверь в страну неопределенного поведения, и это baaad.