पाठ 13 / 25

Traits and Generics

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.

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.

त्वरित जाँच: What does Box<dyn Shape> enable?

  • Faster static dispatch
  • 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.