---
title: "16.4 impl Trait"
description: "The impl Trait shorthand for some type implementing a trait, in argument and return position, and when it beats spelling out a generic parameter."
url: "https://learnrust.net/chapter-16/impl-trait/"
last_updated: "2026-06-13"
---

# 16.4 impl Trait

Trait bounds like `<T: Summary>` are precise but sometimes heavier than a simple case needs. Rust offers a lighter syntax, `impl Trait`, that reads as "some type that implements this trait." It works in two places, function arguments and return types, and each solves a real ergonomic problem. It's not a separate feature so much as a convenient spelling of what you already learned.

## impl Trait in argument position

When a function takes a generic parameter it uses only through a trait, and doesn't need to name the type, you can replace `<T: Trait>` with `impl Trait` right in the parameter list:

```rust
trait Summary {
    fn summarize(&self) -> String;
}

fn notify(item: &impl Summary) {
    println!("Breaking news: {}", item.summarize());
}
```

`item: &impl Summary` means "a reference to some type that implements `Summary`." This is exactly equivalent to the generic form:

```rust
fn notify<T: Summary>(item: &T) {
    println!("Breaking news: {}", item.summarize());
}
```

The `impl Trait` version is shorter and reads more directly for the common case of "I just need something summarizable." Use it when you have one parameter, use it through its trait, and don't need to refer to its type by name elsewhere in the signature. Reach for the explicit `<T: Trait>` form when you need to name `T` (say, two parameters that must be the *same* type, which `fn pair<T: Summary>(a: &T, b: &T)` expresses but two separate `impl Summary`s do not).

## impl Trait in return position

The more powerful use is as a *return* type, where it solves a problem the explicit generic form can't. Often a function returns a value whose type is real but tedious or impossible to write out, especially with closures and iterators (chapter 19). `impl Trait` lets you promise the *capability* without naming the type:

```rust
fn make_greeter() -> impl Fn(&str) -> String {
    |name| format!("Hello, {name}!")
}

fn main() {
    let greet = make_greeter();
    println!("{}", greet("Ada"));
}
```

```
Hello, Ada!
```

`make_greeter` returns `impl Fn(&str) -> String`, read "some type that is a function from `&str` to `String`." The actual returned thing is a closure (chapter 19), whose concrete type has no name you could write. `impl Trait` in return position says "I'm returning *something* with this behavior; you don't need to know its exact type, only what it can do." This is the standard way to return closures and iterators, and you'll see it everywhere once chapter 19 makes those central.

> **Key insight**
>
> `impl Trait` in return position lets a function hide its concrete return type behind a capability. The caller learns "you get something that's a `Fn`" or "something that's an `Iterator`", which is all they need, while the function keeps the freedom to return whatever specific type is convenient. It's the return-value counterpart to taking `&impl Summary` as a parameter: name the behavior, not the type.

## A limitation worth knowing

`impl Trait` in return position has one catch: a function can return only *one* concrete type, even though that type is unnamed. This compiles fine (every path returns the same closure type), but a function that tried to return *either* a closure *or* a different `impl Fn` from different branches would be rejected, because `impl Trait` is still one hidden concrete type chosen by monomorphization (lesson [15.5](https://learnrust.net/chapter-15/monomorphization/)), not a runtime choice between types.

```rust
fn make_op(add: bool, by: i32) -> impl Fn(i32) -> i32 {
    if add {
        move |x| x + by
    } else {
        move |x| x - by     // refused: a different closure type than the other arm
    }
}
```

```
error[E0308]: `if` and `else` have incompatible types
 --> src/main.rs:3:9
  |
2 |       if add {
  |  _____________-
3 | |         move |x| x + by
  | |         --------------- expected because of this
4 | |     } else {
5 | |         move |x| x - by
  | |         ^^^^^^^^^^^^^^^ expected closure, found a different closure
6 | |     }
  | |_____- `if` and `else` have incompatible types
  |
  = note: no two closures, even if identical, have the same type
  = help: consider boxing your closure and/or using it as a trait object
```

Each closure captures `by`, so they're genuine distinct closure types (non-capturing closures would quietly coerce to a shared function-pointer type and slip through). The two closures have different concrete types, so they can't both be the single hidden type `impl Fn` stands for. When you genuinely need to return one of several different types decided at runtime, that's the job of a trait object (`Box<dyn Trait>`, lesson [16.9](https://learnrust.net/chapter-16/trait-objects/)), which the error here will eventually point you toward. For the common case of returning one consistent unnamed type, `impl Trait` is exactly right.

> **Best practice**
>
> Use `impl Trait` in argument position for simple "takes something with this behavior" parameters, and the explicit `<T: Trait>` form when you need to name the type or constrain several parameters to the same one. Use `impl Trait` in return position to return closures and iterators whose types are unwieldy or unnameable. When you need to return genuinely different types from different branches, switch to `Box<dyn Trait>` (lesson [16.9](https://learnrust.net/chapter-16/trait-objects/)).

## Quiz time

**Question #1**

Rewrite `fn notify<T: Summary>(item: &T)` using `impl Trait`, and say when you'd prefer the original generic form.

<details class="solution">
<summary>Show solution</summary>

`fn notify(item: &impl Summary)`. The two are equivalent. Prefer the explicit `<T: Summary>` form when you need to *name* the type, for instance to require two parameters to be the same type (`fn f<T: Summary>(a: &T, b: &T)`), which two separate `&impl Summary` parameters wouldn't enforce.

</details>

**Question #2**

Why is `impl Trait` especially useful as a *return* type?

<details class="solution">
<summary>Show solution</summary>

Because some return values (closures, iterators) have concrete types that are tedious or impossible to write by name. `impl Trait` in return position lets the function promise the *behavior* ("returns something that's a `Fn`" or "an `Iterator`") without naming the type, which is the standard way to return closures and iterators.

</details>

**Question #3**

Why is returning `impl Fn(...)` from two different `if`/`else` branches with two different closures refused?

<details class="solution">
<summary>Show solution</summary>

Because `impl Trait` in return position is still a single hidden *concrete* type (chosen at compile time, lesson [15.5](https://learnrust.net/chapter-15/monomorphization/)), and two different closures have two different concrete types. The function must return one consistent type. To return genuinely different types decided at runtime, use a trait object like `Box<dyn Fn(...)>` (lesson [16.9](https://learnrust.net/chapter-16/trait-objects/)).

</details>

You've implemented traits by hand and bounded generics with them. But for common traits like `Debug` and `Clone`, writing the implementation by hand would be pure boilerplate. The next lesson is the tour of `derive`: which traits you can ask the compiler to write for you, and what each one unlocks.

## Sitemap

See the full [sitemap](https://learnrust.net/sitemap.md) for all pages.
