Rust: Elevator Pitch for `impl Trait`

 — 1 minute read


According to the announcement of Rust 1.26 in May, 2018, impl Trait the elevator pitch is this:

fn foo() -> impl Trait {
   // ...
}

This type signature says ”foo is a function that takes no arguments but returns a type that implements the Trait trait.” That is, we’re not going to tell you what the return type of foo actually is, only that it implements a particular trait. You may wonder how this differs from a trait object:

Why is this useful? One good use is closures. Remember that closures in Rust all have a unique, un-writable type, yet implement the Fn trait. This means that if your function returns a closure, you can do this:

// before
fn foo() -> Box<Fn(i32) -> i32> {
    Box::new(|x| x + 1)
}

// after
fn foo() -> impl Fn(i32) -> i32 {
    |x| x + 1
}
No boxing, no dynamic dispatch. A related scenario happens when returning iterators.

But for all the developers using dynamic dispatch by default (like in Java, Python etc.), keep in mind this does evaluate at compile time and not dynamically at runtime. Therefore:

It’s important to note that sometimes trait objects are still what you need. You can only use impl Trait if your function returns a single type; if you want to return multiple, you need dynamic dispatch. For example:

fn foo(x: i32) -> Box<Iterator<Item = i32>> {
    let iter = vec![1, 2, 3]
        .into_iter()
        .map(|x| x + 1);

    if x % 2 == 0 {
        Box::new(iter.filter(|x| x % 2 == 0))
    } else {
        Box::new(iter)
    }
}