Generic Functions and Types
Generics allow writing code that works with multiple types. They are compiled with monomorphization - the compiler generates specific versions for each type used, resulting in zero runtime overhead.
// Generic function
fn largest(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest { largest = item; }
}
largest
}
// Generic struct
#[derive(Debug)]
struct Pair {
first: T,
second: T,
}
impl Pair {
fn new(first: T, second: T) -> Self { Pair { first, second } }
fn max(&self) -> &T {
if self.first > self.second { &self.first } else { &self.second }
}
fn swap(self) -> Pair { Pair::new(self.second, self.first) }
}
// Generic enum (you already know Option and Result)
#[derive(Debug)]
enum Either { Left(L), Right(R) }
fn divide(a: f64, b: f64) -> Either {
if b == 0.0 { Either::Right("division by zero".to_string()) }
else { Either::Left(a / b) }
}
fn main() {
let nums = vec![34, 50, 25, 100, 65];
println!("Largest: {}", largest(&nums));
let strs = vec!["hello", "world", "rust"];
println!("Largest: {}", largest(&strs));
let p = Pair::new(5, 10);
println!("Max: {}", p.max());
match divide(10.0, 3.0) {
Either::Left(result) => println!("{:.4}", result),
Either::Right(err) => println!("Error: {}", err),
}
} 🎯 Practice
- Write generic fn min_max<T: PartialOrd + Copy>(v: &[T]) -> Option<(T, T)>
- Write generic struct Stack<T> with push, pop, peek, is_empty
- Write generic fn unique<T: Eq + std::hash::Hash>(v: Vec<T>) -> Vec<T> removing duplicates
🎉 Key Takeaways
- Generics are monomorphized at compile time - zero runtime cost
- Constraints (T: Trait) specify what the generic type must support
- Generic structs: struct Foo<T>; impl<T> Foo<T>
- Multiple type params: fn zip<A, B>(a: Vec<A>, b: Vec<B>) -> Vec<(A,B)>