Structs, enums and traits

Define data with struct and enum, add methods in impl, share behaviour with traits, and derive Debug.

Runs in Zerethon IDE · Rust 1.87 Official Rust documentation ↗
main.rs
use std::fmt;

#[derive(Debug)]
struct Rect {
    width: f64,
    height: f64,
}

enum Shape {
    Rect(Rect),
    Circle { radius: f64 },
}

trait Area {
    fn area(&self) -> f64;
}

impl Area for Shape {
    fn area(&self) -> f64 {
        match self {
            Shape::Rect(r) => r.width * r.height,
            Shape::Circle { radius } => std::f64::consts::PI * radius * radius,
        }
    }
}

impl Rect {
    fn square(side: f64) -> Self {
        Rect { width: side, height: side }
    }

    fn scale(&mut self, factor: f64) {
        self.width *= factor;
        self.height *= factor;
    }
}

impl fmt::Display for Rect {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Rect({}x{})", self.width, self.height)
    }
}

fn main() {
    let mut r = Rect { width: 3.0, height: 4.0 };
    r.scale(2.0);
    println!("{r} {:?}", Rect::square(1.0));
    let shapes = vec![Shape::Rect(r), Shape::Circle { radius: 1.0 }];
    for s in &shapes {
        println!("{:.2}", s.area());
    }
}

Output

Rect(6x8) Rect { width: 1.0, height: 1.0 }
48.00
3.14
  • Methods take &self (read), &mut self (change) or self (consume). A function in impl without self, like Rect::square, is called on the type.
  • An enum variant can carry data; match takes it apart.
  • A trait is a set of methods a type promises to have, like an interface. Implementing fmt::Display makes {} work; #[derive(Debug)] makes {:?} work.