Type System Fundamentals

Generic Type Parameters

Generic type parameters (<T, U>) abstract structs, enums, traits, functions, and impl blocks over unknown types, enabling parametric polymorphism that is specialized into concrete machine code via monomorphization at compile time.

Where Generic Parameters Can Be Declared

Rust supports three kinds of generic parameters inside <...> lists, ordered as lifetimes first, then types, and const generics, with defaulted parameters last:

Generic Parameter Kind Syntax Example What It Abstracts Over
Lifetime parameter <'a, 'b: 'a> Borrow duration / region of references
Type parameter <T, E = MyError> Any concrete type satisfying declared trait bounds (T: Sized by default)
Const generic parameter <const N: usize> Compile-time constant integer, bool, or char value
#[derive(Debug)]
struct FixedRing<'a, T, const CAP: usize> {
    label: &'a str,
    storage: [T; CAP],
}

impl<'a, T: Copy + Default, const CAP: usize> FixedRing<'a, T, CAP> {
    fn new(label: &'a str) -> Self {
        Self {
            label,
            storage: [T::default(); CAP],
        }
    }
}

fn main() {
    let ring: FixedRing<'_, i32, 4> = FixedRing::new("sensor_buffer");
    println!("{}: {:?}", ring.label, ring.storage);
}
sensor_buffer: [0, 0, 0, 0]

Declaring Bounds Only Where Needed (impl Blocks vs. Struct Headers)

Do not place trait bounds (T: Clone + Debug) on the struct Foo<T> definition itself unless the struct's fields require an associated type from that trait (field: T::Assoc) or the struct implements Drop. Instead, keep struct Foo<T> unconstrained and place trait bounds only on the specific impl<T: Bound> blocks that need them:

struct Pair<T> {
    first: T,
    second: T,
}

// 1. Works for ANY type `T` (even types that do not implement `Ord` or `Display`):
impl<T> Pair<T> {
    fn new(first: T, second: T) -> Self {
        Self { first, second }
    }

    fn into_tuple(self) -> (T, T) {
        (self.first, self.second)
    }
}

// 2. Available ONLY when `T: Ord`:
impl<T: Ord> Pair<T> {
    fn max_ref(&self) -> &T {
        self.first.max(&self.second)
    }
}

fn main() {
    let p = Pair::new(15, 42);
    println!("Max of pair: {}", p.max_ref());
    println!("As tuple:    {:?}", p.into_tuple());
}
Max of pair: 42
As tuple:    (15, 42)