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:

  1. Identify duplicate code
  2. Extract into function body with appropriate signature
  3. Replace duplicated code with function calls

This same pattern applies to generics—replacing specific types with abstract placeholders that work across multiple types.