Impl Trait

Simplify Method Signatures with impl Trait

impl Trait, where impl is a keyword and Trait is a trait name, specifies an unnamed but concrete type that implements a specific trait. It can only appear in argument position (where it can act as an anonymous type parameter to functions) and in return position (where it can act as an opaque return type).

impl Trait is essentially syntactic sugar for a generic type parameter with a trait bound, like <T: Trait>, except that, in argument position, the type is anonymous and doesn't appear in the generic parameter list of a function. In return position, unlike with a generic type parameter, the function, not the caller, chooses the return type:

// Define an example trait and two structs that implement it:
trait Speak {
    fn speak(&self) -> String;
}

struct Dog;
impl Speak for Dog {
    fn speak(&self) -> String {
        "Woof!".to_string()
    }
}

struct Cat;
impl Speak for Cat {
    fn speak(&self) -> String {
        "Meow!".to_string()
    }
}

// Define a function that accepts `impl Trait` in argument position:
fn print_speak(animal: impl Speak) {
    println!("{}", animal.speak());
}

// Define a function with `impl Trait` in return position.
// This function always returns a `Dog`,
// even though the caller only sees `impl Speak`, an opaque type that implements
// `Speak`.
fn get_animal() -> impl Speak {
    Dog
}

// This function would NOT compile if we tried to return different types.
// Use a trait object in that situation:
// fn get_animal(is_dog: bool) -> impl Speak {
//     if is_dog {
//         Dog //  This is one concrete type.
//     } else {
//         Cat // Error: `if` and `else` have incompatible types.
//     }
// }

fn main() {
    let my_dog = Dog;
    let my_cat = Cat;

    print_speak(my_dog); // This call is monomorphized for `Dog`.
    print_speak(my_cat); // This call is monomorphized for `Cat`.

    let some_speaker = get_animal();
    // The return value implements the `Speak` trait:
    println!("The hidden dog says: {}", some_speaker.speak());

    // // ERROR: mismatched types. Expected struct `Dog`, found opaque type
    // `impl Speak`.
    // let dog: Dog = get_animal();
}

Do not confuse impl Trait with dyn Trait. The Trait Objects chapter explains the difference.

Return Opaque Types (esp. Closures and Iterators) with Return-position impl Trait

As discussed above, you can use impl Trait in the return type of a function to indicate that the function returns a type that implements a specific trait, without specifying the exact type.

This is useful when the exact type is complex, not relevant to the caller, or impossible to write explicitly, and especially for closures and iterators:

//! Rust allows to write `impl Trait` as the return type of functions (often
//! called "RPIT"). This means that the function returns "some type that
//! implements the Trait". This is commonly used to return closures, iterators,
//! and other types that are complex or impossible to write explicitly.

/// This function returns a closure that takes an `i32` and returns an `i32`.
fn returns_closure() -> impl Fn(i32) -> i32 {
    |x| x + 1
}

fn main() {
    let f = returns_closure();
    println!("{}", f(1));
}
  • Closures.
  • Generics.
  • Iterators.
  • Traits.
  • Trait Objects.