'(functional software)


Better APIs using Rust's Type System: Follow-Up

The last blog post I wrote about nicer APIs in Rust got some interesting feedback on lobste.rs.

One important thing I forgot to mention was that all the phantom data stuff is zero-sized - it only exists at compile time and has no runtime overhead at all. The compiler will optimize it away completely. (Thanks, Steve Klabnik)

Another person mentioned that Into can be used to generalize the state transitions:

Unfortunately, Into isn't allowed to fail and therefore isn't able to implement many common transitions. As this is a quite common scenario, Rust will get TryFrom and TryInto in the near future. These traits have the same semantics as From and Into, but allow to return a custom error for the failure case.

Using these, it's possible to implement the example from the earlier post like this:

#![feature(try_from)]

use std::marker::PhantomData;
use std::convert::TryFrom;

struct Connection<T> {
  phantom: PhantomData<T>
}

struct Disconnected;
struct Connected;

type ClosedConn = Connection<Disconnected>;
type OpenConn   = Connection<Connected>;

impl ClosedConn {
  fn new() -> ClosedConn {
    Connection { phantom: PhantomData }
  }
}

#[derive(Debug)]
enum TransitionErr {
  Connect,
  Disconnect,
}

impl TryFrom<ClosedConn> for OpenConn {
  type Err = TransitionErr;

  fn try_from(_closed: ClosedConn) -> Result<Self, Self::Err> {
    //Try to connect, for now just return an error
    Err(TransitionErr::Connect)
  }
}

impl TryFrom<OpenConn> for ClosedConn {
  type Err = TransitionErr;

  fn try_from(_open: OpenConn) -> Result<Self, Self::Err> {
    // Disconnect
    Ok(ClosedConn { phantom: PhantomData })
  }
}

fn main() {
  let x = Connection::new();
  let x = OpenConn::try_from(x).expect("Failed to connect");
  let x = ClosedConn::try_from(x).expect("Failed to disconnect");
}

Comments

See lobste.rs or write me an email!