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!