I am trying to implement a state machine for audio playback in Rust. A Playback struct can store a file and whether the playback is playing/paused/stopped, with playing/paused states saving playback position. The code is very simple:
struct PlaybackProgress {
playback_position: f32,
stream_id: usize
}
enum PlaybackState {
Stopped,
Playing(PlaybackProgress),
Paused(PlaybackProgress)
}
struct Playback {
file: Option<String>,
state: PlaybackState,
}
impl Playback {
// Creates a new empty Playback instance (no file, stopped)
pub fn new() -> Self {
Playback {
file: None,
state: PlaybackState::Stopped
}
}
pub fn play(&mut self, file: String) -> bool {
match &self.state {
PlaybackState::Playing(_) => {
false
}
PlaybackState::Paused(progress) => {
self.state = PlaybackState::Playing(progress); // THE ISSUE
true
}
PlaybackState::Stopped => {
self.file = Some(file);
self.state = PlaybackState::Playing(PlaybackProgress {
playback_position: 0.0,
stream_id: 1
});
true
}
}
}
}
The issue is changing a Paused variant to a Playing while keeping the same progress. When trying match self.state, the compiler doesn't allow moving progress, and suggests borrowing (i.e. match &self.state), in which case there is a type mismatch between a reference and value.
I am confused why the issue is there, since the moved value would still be owned by parent struct and have the same lifetime as before.
What's a simple readable way to achieve this?
Note: I have read this question and its answers, none of which worked. First answer suggests using an immutable value (impossible since this is a state machine and struct needs to be mutated), or storing a dummy value (impossible, when trying *self.state compiler says it cannot be dereferenced for reasons beyond my understanding). Other questions suggests using unsafe (which I won't do for such a simple task), or Rc<>/Option<>, which don't make sense in this case.