I have written some serial protocol code:
#[derive(Debug, PartialEq, Eq)]
enum State {
WAIT_DLE,
WAIT_STX,
WAIT_DLE_OR_BYTE { payload: Vec<u8> },
WAIT_DLE_OR_ETX { payload: Vec<u8> },
WAIT_CRC { payload: Vec<u8>, crc: u32, remaining: usize },
}
#[derive(Debug, PartialEq, Eq)]
struct Decoder {
state: State,
decoded: u64,
unstuffing_errors: u64,
crc_errors: u64
}
impl Decoder {
fn new() -> Decoder {
Decoder {
state: State::WAIT_DLE,
decoded: 0,
unstuffing_errors: 0,
crc_errors: 0,
}
}
fn decode_byte(&mut self, byte: u8) -> Option<Vec<u8>> {
self.state = match self.state {
State::WAIT_DLE => match byte {
DLE => State::WAIT_STX,
_ => State::WAIT_DLE
},
State::WAIT_STX => match byte {
STX => State::WAIT_DLE_OR_BYTE { payload: Vec::<u8>::new() },
_ => State::WAIT_DLE
},
State::WAIT_DLE_OR_BYTE { mut payload } => match byte {
DLE => State::WAIT_DLE_OR_ETX { payload },
b => {
payload.push(b);
State::WAIT_DLE_OR_BYTE { payload }
}
},
...
The error is:
error[E0507]: cannot move out of `self.state.payload` as enum variant `WAIT_CRC` which is behind a mutable reference
--> common/src/dlecrc32.rs:46:28
|
46 | self.state = match self.state {
| ^^^^^^^^^^ help: consider borrowing here: `&self.state`
...
55 | State::WAIT_DLE_OR_BYTE { mut payload } => match byte {
| ----------- data moved here
...
62 | State::WAIT_DLE_OR_ETX { mut payload } => match byte {
| ----------- ...and here
...
73 | State::WAIT_CRC { payload, mut crc, remaining } => {
| ------- ...and here
|
= note: move occurs because these variables have types that don't implement the `Copy` trait
Its advice to use a &self.state reference is not helpful, as many paths need to rip the payload: Vec<u8> out of the old state and put it into the new state. (Copying the vector's contents would be wasteful, as its old owner is being dropped.)
I can fix the problem by using mem::swp() to temporarily put a dummy value into state while dismembering the old state:
fn decode_byte(&mut self, byte: u8) -> Option<Vec<u8>> {
let mut swapped_state = State::WAIT_DLE;
mem::swap(&mut self.state, &mut swapped_state);
self.state = match swapped_state {
This is wasteful, as it is copying the bytes of self.state when that is not required.
Another unattractive solution is to make the State properties properties of the Decoder, turning the State enum into a C-style enum.
Is there a way to let the compiler accept that as I'm assigning to self.state, it's OK for me to dismember the old self.state?