Generic Types, Traits, and Lifetimes
Rust’s generics provide compile-time polymorphism through abstract type parameters. This chapter covers three key concepts: generic types for code reuse, traits for shared behavior contracts, and lifetimes for memory safety guarantees.
You’ve already used generics with Option<T>
, Vec<T>
, HashMap<K, V>
, and Result<T, E>
. Now you’ll learn to define your own generic types, functions, and methods.
Removing Duplication by Extracting a Function
Before exploring generics, let’s examine the pattern of extracting common functionality. Consider finding the largest number in a list:
fn main() { let number_list = vec![34, 50, 25, 100, 65]; let mut largest = &number_list[0]; for number in &number_list { if number > largest { largest = number; } } println!("The largest number is {largest}"); assert_eq!(*largest, 100); }
To handle multiple lists without code duplication, extract the logic into a function:
fn largest(list: &[i32]) -> &i32 { let mut largest = &list[0]; for item in list { if item > largest { largest = item; } } largest } fn main() { let number_list = vec![34, 50, 25, 100, 65]; let result = largest(&number_list); println!("The largest number is {result}"); assert_eq!(*result, 100); let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8]; let result = largest(&number_list); println!("The largest number is {result}"); assert_eq!(*result, 6000); }
The refactoring process:
- Identify duplicate code
- Extract into function body with appropriate signature
- Replace duplicated code with function calls
This same pattern applies to generics—replacing specific types with abstract placeholders that work across multiple types.