Rust Generics

Generics let you write code that works with many different types without writing a separate version for each one. They eliminate repetition while keeping type safety. Rust uses generics extensively in its standard library — Vec<T>, Option<T>, and Result<T, E> are all generic types you have already used.

The Problem Without Generics

Without generics, you need separate functions for each type:

fn largest_i32(list: &[i32]) -> i32 { ... }
fn largest_f64(list: &[f64]) -> f64 { ... }
fn largest_char(list: &[char]) -> char { ... }

The logic is identical, only the type changes. Generics let you write this once.

Generic Functions

Replace the concrete type with a type parameter, written inside angle brackets <>:

fn largest<T: PartialOrd>(list: &[T]) -> &T {
    let mut biggest = &list[0];
    for item in list {
        if item > biggest {
            biggest = item;
        }
    }
    biggest
}

fn main() {
    let numbers = vec![34, 50, 25, 100, 65];
    println!("Largest: {}", largest(&numbers));

    let chars = vec!['y', 'm', 'a', 'q'];
    println!("Largest: {}", largest(&chars));
}

Output:

Largest: 100
Largest: y

The constraint T: PartialOrd tells Rust that T must support comparison with >. Without this constraint, the compiler would reject the comparison.

The Universal Remote Diagram

Without generics:          With generics:
  remote_for_tv()            press_button<T>(device: T)
  remote_for_ac()               ↑
  remote_for_fan()           Works for any device type T
  (one function per device)  (one function for all)

Generic Structs

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

fn main() {
    let int_pair = Pair { first: 5, second: 10 };
    let str_pair = Pair { first: "hello", second: "world" };
}

The same struct works for any type. Both fields must be the same type T. Use two type parameters for different types:

struct KeyValue<K, V> {
    key: K,
    value: V,
}

let entry = KeyValue { key: "name", value: 42 };

Generic Enums

You have already used these. Option and Result are generic enums defined in the standard library:

enum Option<T> {
    Some(T),
    None,
}

enum Result<T, E> {
    Ok(T),
    Err(E),
}

Implementing Methods on Generic Types

impl<T> Pair<T> {
    fn new(first: T, second: T) -> Self {
        Pair { first, second }
    }
}

impl<T: std::fmt::Display + PartialOrd> Pair<T> {
    fn print_largest(&self) {
        if self.first >= self.second {
            println!("First is larger: {}", self.first);
        } else {
            println!("Second is larger: {}", self.second);
        }
    }
}

The first impl works for any T. The second impl adds an extra method only available when T implements Display and PartialOrd.

Trait Bounds

Trait bounds constrain which types are allowed for a generic parameter. They appear after a colon following the type parameter:

fn print_item<T: std::fmt::Display>(item: T) {
    println!("{}", item);   ← Works only if T can be displayed
}

Multiple bounds use +:

fn compare_and_display<T: PartialOrd + std::fmt::Display>(a: T, b: T) {
    if a > b {
        println!("{} is greater than {}", a, b);
    }
}

Zero-Cost Abstraction

Rust's generics compile using a technique called monomorphization. When you call largest(&numbers) with i32, the compiler generates a concrete version of largest specialized for i32. The same happens for every other type you use. The result is code as fast as if you had written each version manually.

What the Compiler Does

You write:           fn largest<T>(list: &[T]) -> &T

Compiler generates:  fn largest_i32(list: &[i32]) -> &i32
                     fn largest_f64(list: &[f64]) -> &f64
                     fn largest_char(list: &[char]) -> &char

You pay no runtime cost for using generics.

Quick Reference

fn f<T>(x: T) { }                      ← Generic function
fn f<T: Trait>(x: T) { }              ← With trait bound
struct S<T> { field: T }               ← Generic struct
impl<T> S<T> { fn method() }          ← Implement on generic struct
struct Kv<K, V> { key: K, val: V }    ← Two type parameters

Leave a Comment