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

Datetime:2016-08-22 23:58:57          Topic: Rust           Share

Thelast 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");
}




About List