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!