Using Rust's Type System for Better APIs
I recently started enjoying programming some hobby projects in Rust. The type system is pretty strong, but doesn't get in your way/ The error messages are very good compared to other languages and it doesn't need a pesky VM to run.
Some days ago I stumbled over a blog post by InsanityBit where he details how to write a type-safe API interface that models the state machine of IMAP. With his approach, Rust will check at compile time if the the user tries to call a function that isn't allowed in the current state.
I wondered if something like this could be implemented with PhantomTypes. This is what I came up with:
use std::marker::PhantomData;
struct Connection<T> {
phantom: PhantomData<T>
}
struct Disconnected;
struct Connected;
impl Connection<Disconnected> {
fn new() -> Connection<Disconnected> {
Connection { phantom: PhantomData }
}
fn connect(self) -> Connection<Connected> {
Connection { phantom: PhantomData }
}
}
impl Connection<Connected> {
fn disconnect(self)-> Connection<Disconnected> {
Connection { phantom: PhantomData }
}
}
fn main() {
let x: Connection<Disconnected> = Connection::new();
x.disconnect(); // compile-time error
let x = x.connect(); // works, x is now Connection<Connected>
x.connect(); // compile-time error
let x = x.disconnect(); // works, x is now Connection<Disconnected>
}
This is especially cool when modeling state machines. It allows us to
model functions specific to a single state. We can even model different
transition_to()
functions and the type system will make sure we can't
do an invalid transition!
Comments
See lobste.rs or feel write me an email!