Thuta Learning
Rust
IntermediateProgrammingbeginner

Generics and Monomorphization

What you'll walk away with

  • Explain the core ideas behind Generics and Monomorphization
  • Run the sample Rust code and verify its output
  • Apply the technique correctly to the Tutorial Platform and production scenarios

Build the mental model

Without generics, a function that finds the largest value would need to be duplicated for `i32`, `f64`, `String`, and so on — a generic type parameter (`fn largest<T: PartialOrd>(list: &[T]) -> &T`) lets you write that logic exactly once for any of those types, with `T` acting as a placeholder resolved to a concrete type only at compile time. Java or TypeScript generics get erased at runtime (no generic info survives into compiled code, so checks happen at runtime), but Rust generics are entirely different: the compiler generates a specific version of a generic function for every concrete type it's actually used with (`i32`, `f64`, `String`) via a process called monomorphization — so generic code performs exactly as fast as hand-written duplicate code with zero runtime overhead, a true zero-cost abstraction. If a generic type parameter has no trait bound (`T: PartialOrd`, covered in depth in Lesson 14), the compiler knows nothing about `T`'s behavior and won't allow any operation on it, not even `>` comparison — a generic type should never be thought of as accepting "anything at all"; a trait bound is exactly what declares which operations are actually available.

Connect it to a real scenario

Write the content analyzer's "find the maximum value in a list" logic as a single generic function `fn find_max_stat<T: PartialOrd>(items: &[T]) -> Option<&T>`, reusable for both word-count (`u32`) lists and average-sentence-length (`f64`) lists. Because of monomorphization, distinct `find_max_stat::<u32>` and `find_max_stat::<f64>` versions get generated separately at compile time, so there is no runtime performance loss at all — it performs exactly as if you'd hand-written two duplicate functions. If you design a generic `Stats<T>` struct to hold min/max/average, you must specify the trait bound `T: PartialOrd + Copy` — without it, the compiler won't accept the min/max comparison operations at all.

Try the working example

rust
fn find_max_stat<T: PartialOrd>(items: &[T]) -> Option<&T> {
    let mut max = items.first()?;
    for item in items {
        if item > max {
            max = item;
        }
    }
    Some(max)
}

fn main() {
    let word_counts = [420, 890, 615, 1200];
    let avg_lengths = [4.2, 5.8, 3.9];

    println!("{:?}", find_max_stat(&word_counts));
    println!("{:?}", find_max_stat(&avg_lengths));
}
You should see
You print `Some(1200)` and `Some(5.8)`, proving the one function works for both types.

5-minute try-it

Write a generic function `fn average<T: Into<f64> + Copy>(items: &[T]) -> f64` that returns a list's average as an f64 — test it with both a `u32` list and an `i32` list.

One important caution

Writing a generic function with no trait bound at all, `fn largest<T>(list: &[T]) -> T`, then trying to use `>` comparison — the compiler reports "binary operation `>` cannot be applied"; a `T: PartialOrd` bound is required.

Assuming generic code shares one implementation at runtime the way type-erased Java generics do — Rust's monomorphization can unexpectedly grow binary size ("code bloat") since a separate version compiles per concrete type used.

The Rust Programming Language — Generic Data TypesRust

Easy traps

  • Writing a generic function with no trait bound at all, `fn largest<T>(list: &[T]) -> T`, then trying to use `>` comparison — the compiler reports "binary operation `>` cannot be applied"; a `T: PartialOrd` bound is required.
  • Assuming generic code shares one implementation at runtime the way type-erased Java generics do — Rust's monomorphization can unexpectedly grow binary size ("code bloat") since a separate version compiles per concrete type used.
  • Validate sample code in a local or test environment before applying it to a production system.

Exercise

Write a generic function `fn average<T: Into<f64> + Copy>(items: &[T]) -> f64` that returns a list's average as an f64 — test it with both a `u32` list and an `i32` list.

You'll know it worked when: You print `Some(1200)` and `Some(5.8)`, proving the one function works for both types.

Generics and Monomorphization | Thuta Learning