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
?