Course contentsShow
← Rust
Lesson 680 of 1,9106. Generics and Static DispatchFree lesson

Code Bloat and Binary Size Considerations

Identify when extensive use of generics impacts binary size due to monomorphization.

In this lesson, you will learn how Rust’s powerful generic system can lead to "code bloat" and why your binary size might grow as you add more type variations.

The Cost of Monomorphization

As we discussed in the Philosophy of Monomorphization, Rust generates a unique, optimized version of a function for every concrete type you use with a generic. This is a "zero-cost abstraction" in terms of runtime performance because the CPU executes native machine code specifically tailored to that type.

However, there is a trade-off: binary size.

When you use a generic function, the compiler copies and pastes the function body for every type you pass into it. If you use the same function for i32, f64, String, and several custom structs, the compiler generates multiple distinct blocks of machine code for that single generic source definition. This expansion is called code bloat.

A Concrete Illustration

Consider a simple function that processes data:

fn process<T: std::fmt::Display>(item: T) {
    println!("Processing: {}", item);
}

fn main() {
    process(10);        // Compiler creates a version for i32
    process(20.5);      // Compiler creates a version for f64
    process("Rust");    // Compiler creates a version for &str
}

In the example above, the compiler generates three separate versions of process. For a tiny function, this is negligible. But if process were a large, complex algorithm containing hundreds of lines of logic, those three versions would add significant weight to your final binary.

Balancing Performance and Size

This isn't necessarily "bad"—in fact, it is exactly why Rust code is fast. However, when working on systems with strict size limits (like embedded devices), you should be aware of this growth. If you find your binary size ballooning, look for generic functions that contain massive amounts of code. If the performance gain of monomorphization isn't critical for those specific parts, you might consider using trait objects (&dyn Trait) to force dynamic dispatch, which uses a single function body for all types at the cost of a small runtime lookup penalty.

Key Takeaway: Monomorphization provides elite runtime performance by generating type-specific code, but each unique type combination increases your binary size—a trade-off known as code bloat.