We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Generics and Traits step 17 of 24
impl Trait in argument position is not a generic
These two signatures accept exactly the same set of arguments:
pub fn generic<T: Debug>(t: T) -> String
pub fn apit(t: impl Debug) -> String
The second is argument-position impl Trait, usually written APIT. It
reads beautifully — the parameter looks like an ordinary parameter, and there
is no angle-bracket tax to pay before you have written your first line of
generic code. That is why it is worth learning before <T: Trait>.
It is not, however, sugar. There is one difference and it matters at the boundary of any API you publish.
The difference
<T: Debug> declares a parameter with a name. Callers can supply it:
generic::<i64>(v)
impl Debug declares an anonymous parameter. There is no name, so there
is nothing for a turbofish to point at:
apit::<String>(v.to_string())
error[E0107]: function takes 0 generic arguments but 1 generic argument
was supplied
|
= note: `apit` has an anonymous type parameter that cannot be
explicitly specified
The starter contains that exact call. Compile it, read E0107, then remove the turbofish. That is the whole lesson: if you publish an API using APIT and a caller ever needs to pin the type down, they cannot, and you will have to break the signature to let them.
The second difference follows from the first. Two impl Trait parameters are
two independent anonymous types:
fn pair(a: impl Debug, b: impl Debug) // a and b may differ
fn pair<T: Debug>(a: T, b: T) // a and b MUST be the same type
With <T> you can express “these two arguments have the same type”. With APIT
you cannot, because there is no name to repeat.
Your task
pub fn generic<T: Debug>(t: T) -> String
pub fn apit(t: impl Debug) -> String
pub fn run(vals: Vec<i64>) -> Vec<String>
For each value, produce "{named}|{anon}" where named is
generic::<i64>(v) — keep the turbofish, it is legal and it is half the
demonstration — and anon is apit(v.to_string()).
Watch the expected output. For v = 1 the answer is:
1|"1"
The quotes are real. {:?} on an i64 prints 1; {:?} on a String
prints "1", with quotes and with escaping, because Debug is for
programmers and a programmer wants to see where the string ends. This is the
single most useful practical difference between {} and {:?}.
When to use which
-
APIT for internal helpers and for parameters nobody will ever need to
name —
fn log(msg: impl Display). It is shorter and it reads better. -
<T: Trait>the moment the type appears more than once, or the moment a caller might want to choose it. Public APIs default to this.
There is a clippy::impl_trait_in_params lint that flags APIT in public
signatures, but it belongs to the restriction group — allow by default and
never enabled wholesale — so it will not gate you here. Restriction lints are
opinions, not rules; several of them contradict each other by design.
Remember the grade is compile + tests + clippy -D warnings.
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.