JSTAcademy
0 XP
Dashboard
Crash Courses
Traits & Generics
40 min
Masters+175 XP
Crash Courses · Masters

Traits & Generics

Write reusable, type-safe abstractions without runtime cost
40 min read+175 XP on completionCert: Rust Crash Course
Tap any word in the text below to start reading from there.

Traits & Generics

Defining a Trait

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

    // Default implementation
    fn preview(&self) -> String {
        format!("Read more: {}", self.summarize())
    }
}

struct Article {
    title:   String,
    content: String,
}

impl Summary for Article {
    fn summarize(&self) -> String {
        format!("{}: {}...", self.title, &self.content[..50.min(self.content.len())])
    }
}

fn main() {
    let a = Article {
        title:   "Rust is fast".into(),
        content: "Rust achieves performance without a garbage collector.".into(),
    };
    println!("{}", a.summarize());
    println!("{}", a.preview()); // uses default implementation
}

Generics with Trait Bounds

use std::fmt::Display;

// T must implement both Display and PartialOrd
fn largest<T: PartialOrd + Display>(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)); // 100

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

impl Trait (modern shorthand)

// In parameter position — accepts any type implementing Display
fn print_item(item: &impl Display) {
    println!("{}", item);
}

// In return position — return some type implementing Iterator
fn make_counter(n: u32) -> impl Iterator<Item = u32> {
    (0..n)
}

Generic Structs

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

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

dyn Trait (Dynamic Dispatch)

Use Box<dyn Trait> when you need a collection of heterogeneous types:

fn make_shapes() -> Vec<Box<dyn Summary>> {
    vec![
        Box::new(Article { title: "A".into(), content: "...".into() }),
        // Box::new(Tweet { ... }),
    ]
}

Dynamic dispatch has a small runtime cost (vtable lookup) vs the zero-cost of generics (monomorphization). Use generics when you know the type at compile time; dyn when you need heterogeneous collections.

0%