Skip to content

01. Generics, Traits & Lifetimes

To write truly reusable and flexible Rust code, we must master Generics, Traits, and Lifetimes—the tools for high-level abstractions.

1. Generics

Generics allow you to write functions and structs that work with any type, without sacrificing performance (monomorphization).

struct Point<T> {
    x: T,
    y: T,
}

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

2. Traits (Interfaces)

A Trait defines shared behavior for types. It’s similar to interfaces in other languages but more powerful.

trait Summary {
    fn summarize(&self) -> String;
}

struct NewsArticle {
    headline: String,
    author: String,
}

impl Summary for NewsArticle {
    fn summarize(&self) -> String {
        format!("{} by {}", self.headline, self.author)
    }
}

Trait Bounds

Trait bounds specify that a generic type must implement certain behaviors.

fn notify<T: Summary>(item: &T) {
    println!("Breaking news! {}", item.summarize());
}

3. Lifetimes

Lifetimes are a way to ensure that all references are valid for as long as they are used. Most lifetimes are inferred by the compiler, but sometimes we need to annotate them.

// 'a is a lifetime annotation.
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

This tells the compiler that the returned reference will last at least as long as the smallest lifetime of the inputs.

4. Smart Pointers

Smart pointers are data structures that act like pointers but have additional metadata and capabilities.

  • Box<T>: For allocating values on the heap.
  • Rc<T>: Reference Counting for multiple ownership.
  • Arc<T>: Atomic Reference Counting (thread-safe Rc).
  • RefCell<T>: Allows for interior mutability even with immutable references.

Advanced concepts continue with Fearless Concurrency—Rust’s unique approach to multi-threading.