Generics
Generics parameterize types and functions so one implementation works for many concrete types while staying type-safe and zero-cost.
Recipe
fn largest<T: PartialOrd>(list: &[T]) -> &T {
list.iter().max_by(|a, b| a.partial_cmp(b).unwrap()).unwrap()
}
struct Wrapper<T>(T);
impl<T: std::fmt::Debug> Wrapper<T> {
fn debug(&self) { println!("{:?}", self.0); }
}When to reach for this: Containers, algorithms, and APIs reusable across types without dyn overhead.
Working Example
struct Page<T> { items: Vec<T>, page: u32 }
impl<T> Page<T> {
fn new(items: Vec<T>) -> Self {
Self { items, page: 1 }
}
fn len(&self) -> usize { self.items.len() }
}
fn main() {
let p = Page::new(vec![1, 2, 3]);
println!("{}", p.len());
}What this demonstrates:
- Generic struct
Page<T> - Methods available for any
T - Monomorphization generates specialized code per
T - Zero runtime type erasure unless
dyn
Deep Dive
Generic Parameters
Functions, structs, enums, impls, and traits accept type params <T>, const params <const N: usize>, and lifetimes 'a.
Turbofish
let v = Vec::<i32>::new();
let s = "42".parse::<u32>().unwrap();Const Generics
struct Buffer<const N: usize> { data: [u8; N] }Gotchas
- Monomorphization bloat - Many
Tinflate binary size. Fix: Dynamic dispatchdyn Traitor type erasure at boundary. - Missing trait bounds -
Thas no methods. Fix: AddT: Traitbounds. - Over-generic APIs - Hard to read. Fix: Concrete types at app boundary, generic inside library.
- Const generic inference limits - Sometimes need explicit
::<N>. Fix: Annotate const param. - Lifetime + type params together - Signature complexity. Fix:
whereclause formatting.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
dyn Trait | Runtime heterogeneity | Hot path needs inline |
| Macros | Generate per-type code | Generics expressible |
enum of types | Closed set of variants | Open-ended T |
| Type alias | Simplify long generics | Only one instantiation used |
FAQs
Runtime cost?
Monomorphized - no runtime type info unless `dyn`.How many type params?
No hard limit; readability usually stops at few.Generic enums?
`enum E<T> { Some(T), None }` is `Option`.impl generic struct?
`impl<T> Foo<T> { }` inherent methods.Default type params?
`struct S<T = u32>(T)`.Associated types vs generics?
Associated types one per impl; generics more flexible per fn.Variance?
Advanced - lifetimes variance matters for subtyping.Generic lint?
clippy type_complexity warns on long signatures.no_std generics?
Same system without std collections.async generics?
Async fns generic over `T: Trait` common.Related
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.