# Traits and Generics — Rust

Source: https://www.skillbyai.com/en/rust/t-traits

> Shared behaviour, static or dynamic dispatch.

## impl Trait for Type

A **trait** declares methods a type can implement, optionally with default implementations; `impl Shape for Rect` provides them. **Generics** with **trait bounds** (`fn largest<T: PartialOrd + Copy>`) work for any type meeting the bounds and are compiled to specialised code (static dispatch, no runtime cost). **Trait objects** (`Box<dyn Shape>`) allow mixing different types in one collection at the cost of dynamic dispatch. Implementing standard traits such as `Display` integrates your types with the language (formatting with `{}`).

## A Shape trait, Display, generics and trait objects, run

I ran this with Rust 1.99.0 (cargo run, edition 2024, standard library only). A vector of boxed trait objects holds a Rect and a Circle and sums their areas; Circle uses the default name method; Display controls {} printing; the generic largest works for integers and floats.

```rust
use std::fmt;

trait Shape {
    fn area(&self) -> f64;
    fn name(&self) -> String { "shape".to_string() } // default method
}

struct Rect { w: f64, h: f64 }
struct Circle { r: f64 }

impl Shape for Rect {
    fn area(&self) -> f64 { self.w * self.h }
    fn name(&self) -> String { "rect".to_string() }
}
impl Shape for Circle {
    fn area(&self) -> f64 { std::f64::consts::PI * self.r * self.r }
}
impl fmt::Display for Circle {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Circle(r={})", self.r) }
}

fn largest<T: PartialOrd + Copy>(items: &[T]) -> T { // generic with trait bounds
    let mut m = items[0];
    for &x in items { if x > m { m = x; } }
    m
}

fn total_area(shapes: &[Box<dyn Shape>]) -> f64 {      // dynamic dispatch
    shapes.iter().map(|s| s.area()).sum()
}

fn main() {
    let shapes: Vec<Box<dyn Shape>> = vec![Box::new(Rect { w: 3.0, h: 4.0 }), Box::new(Circle { r: 1.0 })];
    println!("total area {:.2}", total_area(&shapes));
    println!("{} {}", shapes[0].name(), shapes[1].name());
    println!("{}", Circle { r: 2.0 });
    println!("{} {:.1}", largest(&[3, 9, 4]), largest(&[1.5, 0.5]));
}
```

Output:

```
total area 15.14
rect shape
Circle(r=2)
9 1.5
```

## Prefer generics, use dyn when mixing types

Generics are zero-cost; reach for dyn Trait when you need a heterogeneous collection or smaller binaries.

**Quiz:** What does Box<dyn Shape> enable?

- [ ] Faster static dispatch
- [x] Storing different types that implement Shape in one collection
- [ ] Avoiding the trait entirely
- [ ] Garbage collection

*Answer:* Storing different types that implement Shape in one collection. Trait objects use dynamic dispatch.
