How to Implement the Typestate Pattern in Rust
Implement the Typestate pattern by defining an enum where each variant represents a valid state and using match to enforce state transitions at compile time.
use std::mem;
enum TcpStream {
Unconnected,
Connected,
Writing,
Closed,
}
impl TcpStream {
fn connect(&mut self) {
match self {
TcpStream::Unconnected => *self = TcpStream::Connected,
_ => panic!("Cannot connect"),
}
}
fn write(&mut self, _data: &[u8]) {
match self {
TcpStream::Connected => *self = TcpStream::Writing,
_ => panic!("Cannot write"),
}
}
fn close(&mut self) {
match self {
TcpStream::Writing | TcpStream::Connected => *self = TcpStream::Closed,
_ => panic!("Cannot close"),
}
}
}
fn main() {
let mut stream = TcpStream::Unconnected;
stream.connect();
stream.write(b"hello");
stream.close();
}
This approach ensures that invalid state transitions (like writing before connecting) are caught by the compiler or runtime logic, preventing bugs.