Rust
Monomorphization
Learn the cost of generics through monomorphization.
Monomorphization
A generic function is compiled separately for each concrete type. Each copy is fully optimized for that type; the price is code bloat and longer compiles:
fn tanim<T: std::fmt::Debug>(x: T) -> String {
format!("{x:?}")
}
tanim(5); // tanim::<i32>
tanim("a"); // tanim::<&str>
tanim(vec![1, 2]); // tanim::<Vec<i32>> → three separate functions
A common way to reduce code bloat in a large generic function is moving the type-independent part into a non-generic inner function.
The inner function pattern
The generic entry is kept thin; the real work happens in a single concrete function:
pub fn oku<P: AsRef<std::path::Path>>(yol: P) -> std::io::Result<String> {
fn ic(yol: &std::path::Path) -> std::io::Result<String> {
// a long, type-independent body
std::fs::read_to_string(yol)
}
ic(yol.as_ref())
}
An alternative: using &dyn Trait on large, rarely called code paths. The cargo bloat tool shows which functions take the most room in the binary.
Exercise Three copies
Write the generic tanim function above. Print the results for 5, "a" and vec![1, 2] separated by | .
Show example solution
fn tanim<T: std::fmt::Debug>(x: T) -> String {
format!("{x:?}")
}
fn main() {
println!("{} | {} | {}", tanim(5), tanim("a"), tanim(vec![1, 2]));
}
Test yourself
-
Why does the standard library's <code>fs::read_to_string<P: AsRef<Path>></code> call a non-generic function inside?
- For speed
- For safety
- Convention
- So the real work isn't compiled again for every P type
Show answer
So the real work isn't compiled again for every P type — The outer generic layer only calls as_ref(); the body stays in a single copy.
-
Shared body
pub fn oku<P: AsRef<Path>>(yol: P) -> io::Result<String> {
fn ic(yol: &Path) -> io::Result<String> { fs::read_to_string(yol) }
ic(yol.____())
}
Show answer
pub fn oku<P: AsRef<Path>>(yol: P) -> io::Result<String> {
fn ic(yol: &Path) -> io::Result<String> { fs::read_to_string(yol) }
ic(yol.as_ref())
}
-
Call and generated copy
- tanim(5)
- tanim::<i32>
- tanim("a")
- tanim::<&str>
- tanim(vec![1, 2])
- tanim::<Vec<i32>>
- tanim(7)
- No new copy (i32 exists)
Next lesson: Iterator fusion