Plingdollar

02 Mar 2019

Optional

A neat feature of the Option<T> type in Rust is that it implements the From<T> trait. This allows for ergonomic optional parameters:

fn print_int<T: Into<Option<u32>>>(stuff: T) {
    match stuff.into() {
        Some(value) => println!("{}", value),
        None => println!("no value!"),
    }
}

This function can be called with a standard Option value like print_int(Some(100)) or print_int(None). The key is that the Into conversion also allows you to call without wrapping values in Some. See it in action in the playground.

In Rust a common pattern when creating complex objects is to use the Builder Pattern. This is useful where you want to begin with some default state and apply partial customisations to it. In many cases however it is a bit heavyweight. Take for example constructing a node for an expression tree. In this case we might already have the components of the tree up front. We want to support parts of the tree being missing. In this case we can create builder functions with Into<Option<T>> arguments make this kind of construction a little more ergonomic:

#[derive(Debug)]
pub enum Expr {
    Literal(Option<i64>),
    Unary(Option<Op>, Option<Box<Expr>>),
    Binary(Option<Box<Expr>>, Option<Op>, Option<Box<Expr>>),
}

pub fn binary<L, O, R>(lhs: L, op: O, rhs: R) -> Expr
    where L: Into<Option<Expr>>,
          O: Into<Option<Op>>,
          R: Into<Option<Expr>>,
{
    Expr::Binary(lhs.into().map(Box::new),
                 op.into(),
                 rhs.into().map(Box::new))
}

When we come to create a tree we don’t have to mess around with Some values and manually Box things:

let tree = syntax_builder::literal(100);
let tree = syntax_builder::unary(None, tree);
let tree = syntax_builder::binary(tree, Op::Plus, None);

Check this out on the playground.