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.