Advanced Type Features

Argument-Position impl Trait

Argument-position impl Trait (APIT) is the syntax fn foo(param: impl Trait) in a function or method parameter list. It introduces an anonymous generic type parameter scoped to that parameter, compiled via monomorphization just like <T: Trait>(param: T).

Desugaring to Generic Type Parameters

Writing impl Trait in a parameter type is almost identical to declaring a generic type parameter in <...>:

Argument-Position `impl Trait` (APIT):
  fn send_notification(target: impl AsRef<str>, msg: impl Display)

Equivalent Explicit Generic Parameter Form:
  fn send_notification<T: AsRef<str>, M: Display>(target: T, msg: M)

Two Behavioral Differences Between APIT and <T: Trait>

Although APIT and explicit generic parameters generate the exact same machine code, they differ in two type-system rules:

1. Two impl Trait Parameters Can Accept Two Different Concrete Types

In fn compare(a: impl Display, b: impl Display), a and b are given two independent anonymous type parameters (<_T1: Display, _T2: Display>), so callers can pass an i32 as a and a &str as b. If both parameters must have the exact same concrete type, you must use a named generic parameter fn compare<T: Display>(a: T, b: T):

use std::fmt::Display;

// APIT: `a` and `b` can have DIFFERENT concrete types:
fn print_any_two(a: impl Display, b: impl Display) {
    println!("any two: {} and {}", a, b);
}

// Named generic `<T>`: `a` and `b` MUST have the SAME concrete type `T`:
fn print_same_two<T: Display>(a: T, b: T) {
    println!("same type: {} and {}", a, b);
}

fn main() {
    // Allowed with APIT (`i32` and `&str`):
    print_any_two(42_i32, "apples");

    // Must be identical types (`i32` and `i32`) for `<T>`:
    print_same_two(10_i32, 20_i32);
}
any two: 42 and apples
same type: 10 and 20

2. Functions Using APIT Cannot Be Called with Explicit Turbofish Type Arguments (E0632)

When a function has both named generic parameters (such as <R: Default>) and an impl Trait parameter (arg: impl AsRef<str>), callers cannot specify explicit turbofish type arguments (func::<u32>("hello")) on that function (E0632):

// ❌ does not compile
fn parse_or_default<R: std::str::FromStr + Default>(input: impl AsRef<str>) -> R {
    input.as_ref().parse().unwrap_or_default()
}

fn main() {
    // Error E0632: cannot provide explicit generic arguments when `impl Trait`
    // is used in argument position!
    let n = parse_or_default::<u32>("123");
    println!("{}", n);
}
error[E0632]: cannot provide explicit generic arguments when `impl Trait` is used in argument position
 --> src/main.rs:9:32
  |
9 |     let n = parse_or_default::<u32>("123");
  |                                ^^^ explicit generic argument not permitted
  |
  = note: see issue #83701 <https://github.com/rust-lang/rust/issues/83701> for more information

Design Rule: If a function has any generic type parameter that callers frequently specify via turbofish (::<T>), use named generic parameters <T, S: AsRef<str>> for all parameters of that function instead of mixing <T> with argument-position impl Trait.