Skip to content

Traits & Generics

Two of Rust’s most powerful features work together to let you write code that is both flexible and safe:

  • Traits define shared behavior — a contract that any type can fulfill.
  • Generics enable parametric code — one function or struct that works for many types.

Together, they let you write expressive abstractions without giving up compile-time guarantees or paying runtime costs.

In many languages you share behavior through inheritance. Rust takes a different approach: you define a trait (a set of method signatures), and any type can implement it independently. This is similar to interfaces in Java or Go, but with richer capabilities like default method bodies.

trait Greet {
fn hello(&self) -> String;
}

Any struct that implements Greet must provide a hello method. The trait is a contract; the struct fulfills that contract.

Without generics you would need a separate largest_i32, largest_f64, and largest_char function — all with identical bodies but different types. Generics let you write this once using a type parameter (written inside angle brackets in code, like fn largest<T: PartialOrd>(a: T, b: T) -> T).

The T: PartialOrd part is a trait bound — it tells the compiler “this function works for any type T that supports comparison.” The compiler then generates a specialized copy for each concrete type you actually use (a process called monomorphization).

LessonWhat you will learn
TraitsDefine shared behavior, default methods, calling trait methods
GenericsGeneric functions, generic structs, trait bounds, where clauses
Trait Objectsdyn Trait, Box<dyn Trait>, static vs dynamic dispatch
LifetimesLifetime annotations, dangling references, elision rules

The snippet below shows a generic function with a trait bound, a custom trait, and a struct that implements it — all the ideas you will study in depth across this module.

fn main() {
let x: i32 = 42;
println!("x = {}", x);
// Generic function: works for any PartialOrd type
fn largest<T: PartialOrd>(a: T, b: T) -> T {
if a > b { a } else { b }
}
println!("largest(3, 7) = {}", largest(3, 7));
println!("largest(3.14, 2.71) = {}", largest(3.14, 2.71));
// Trait usage
trait Greet {
fn hello(&self) -> String;
}
struct Person { name: String }
impl Greet for Person {
fn hello(&self) -> String {
format!("Hello, {}!", self.name)
}
}
let p = Person { name: String::from("Rust") };
println!("{}", p.hello());
}
What is the primary purpose of a trait in Rust?
What does a generic type parameter allow you to do?
Which keyword is used to implement a trait for a struct in Rust?