We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Generics and Traits step 2 of 24
Generic functions and monomorphisation
A generic function is written once and compiled many times. When you write
fn tn<T>() -> &'static str {
std::any::type_name::<T>()
}
T is a type parameter: a placeholder the caller fills in. Rust does not
keep T around at runtime. Instead, for every distinct T your program
actually uses, the compiler stamps out a separate specialised copy of the
function with T replaced by the concrete type. That process is called
monomorphisation — literally “making it one-shaped”.
This is what people mean by Rust’s “zero-cost abstraction” claim. Generic code costs nothing at runtime because by the time the machine sees it, there is no generic code left. The costs are real but they are paid elsewhere: bigger binaries and longer compiles, because five instantiations means five copies of the machine code.
In this exercise you make monomorphisation directly observable.
std::any::type_name::<T>() reports the fully-qualified name of whatever T
the compiler chose for that copy. Different callers, different answers, one
source function.
Your task
pub fn describe_instantiations(kinds: Vec<String>) -> Vec<String>
For each input string, call tn instantiated at the matching type and return
the name it reports. The names are not what you would type in source — they
are the compiler’s canonical paths:
| you ask for | you get |
|---|---|
u8 |
u8 |
i64 |
i64 |
bool |
bool |
String |
alloc::string::String |
Vec<i64> |
alloc::vec::Vec<i64> |
Vec<String> |
alloc::vec::Vec<alloc::string::String> |
Option<u8> |
core::option::Option<u8> |
&str |
&str |
(u8, bool) |
(u8, bool) |
Anything else returns the literal "unknown".
Read that table again: String really does live in the alloc crate and
Option really does live in core. std mostly re-exports them. Seeing the
true paths once makes the standard library’s structure legible.
The turbofish
The starter contains "u8" => tn(), and it does not compile:
error[E0282]: type annotations needed
Nothing in tn() mentions T. Not the arguments (there are none), not the
return type (&'static str regardless). The compiler has no way to guess, and
it refuses to guess. The fix is to say so explicitly with the turbofish:
tn::<u8>()
::<...> is how you supply type arguments in expression position. It looks
odd because the plain < would be ambiguous with the less-than operator, so
the language requires the :: first. You will also see it as
"42".parse::<i32>() and collect::<Vec<_>>().
Two neighbouring errors worth knowing:
-
Supply the wrong number of type arguments —
tn::<u8, bool>()— and you get E0107, “function takes 0 generic arguments but 2 were supplied” (well, 1 in this case). E0107 is the arity error for generics. - Assign the result to the wrong type and you get the familiar E0308.
A gate rule you should know about now
Under clippy -D warnings a type parameter that is never used is an error:
error: type parameter `T` goes unused in function definition
[clippy::extra_unused_type_parameters]
help: consider removing the parameter
A parameter counts as used if it appears in an argument type, in the return
type, in the body, or behind a PhantomData. tn<T> passes because
type_name::<T>() genuinely uses it; fn dead<T>(x: u8) { ... } does not.
One carve-out is worth knowing because it looks like an inconsistency. On clippy 0.1.95 the lint skips functions with an empty body, which keeps the classic static-assertion idiom legal:
fn assert_send<T: Send>() {} // accepted — empty body
fn assert_send<T: Send>(_x: u8) {} // also accepted — still empty
fn nearly<T: Send>(_x: u8) { let _ = 1; } // REJECTED — body is not empty
Add one statement to that third function and the lint fires. If you meet it, the question to ask is “does this parameter do any work?”, not “did I annoy the linter”.
Remember the grade is compile + tests + clippy -D warnings.
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.