Advanced Guide
There are some features which may be helpful when describing a typestate. There are two main features that weren't discussed yet.
Self-transitioning functions
Putting it simply, states may require to mutate themselves without transitioning, or maybe we require a simple getter.
To declare methods for that purpose, we can use functions that take references (mutable or not) to self
.
Consider the following example where we have a flag that can be up or not. We have two functions, one checks if the flag is up, the other, sets the flag up.
#[state] struct Flag {
up: bool
}
impl Flag {
fn is_up(&self) -> bool;
fn set_up(&mut self);
}
As these functions do not change the typestate state, they transition back to the current state.
Non-deterministic transitions
Consider that a typestate relies on an external component that can fail, to model that, one would use Result<T>
.
However, we need our typestate to transition between known states, so we declare two things:
- An
Error
state along with the other states. - An
enum
to represent the bifurcation of states.
#[state] struct Error {
message: String
}
enum OperationResult {
State, Error
}
Inside the enumeration there can only be other valid states and only Unit
style variants are supported.