Lesson 4 of 12
Structs & Enums
Learn how to model data with structs and enums, add with impl blocks, and handle each case with match. Rust uses these instead of classes.
#[derive(Debug)]
struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
// Associated function (like a static method — no 'self')
fn new(width: f64, height: f64) -> Self {
Rectangle { width, height }
}
// Method — 'self' is the instance
fn area(&self) -> f64 {
self.width * self.height
}
fn perimeter(&self) -> f64 {
2.0 * (self.width + self.height)
}
fn is_square(&self) -> bool {
self.width == self.height
}
// Mutable method
fn scale(&mut self, factor: f64) {
self.width *= factor;
self.height *= factor;
}
}
fn main() {
let mut rect = Rectangle::new(10.0, 5.0);
println!("{:?}", rect); // Rectangle { width: 10.0, height: 5.0 }
println!("Area: {}", rect.area()); // Area: 50
rect.scale(2.0);
println!("Scaled: {:?}", rect); // Rectangle { width: 20.0, height: 10.0 }
}Rust's enums are far more powerful than enums in most languages — each variant can hold different types of data. Combined with pattern matching, they make Rust code extremely expressive.
#[derive(Debug)]
enum Shape {
Circle(f64), // radius
Rectangle(f64, f64), // width, height
Triangle { base: f64, height: f64 }, // named fields
}
impl Shape {
fn area(&self) -> f64 {
match self {
Shape::Circle(r) => std::f64::consts::PI * r * r,
Shape::Rectangle(w, h) => w * h,
Shape::Triangle { base, height } => 0.5 * base * height,
}
}
}
fn main() {
let shapes = vec![
Shape::Circle(5.0),
Shape::Rectangle(4.0, 6.0),
Shape::Triangle { base: 3.0, height: 8.0 },
];
for shape in &shapes {
println!("{:?} has area {:.2}", shape, shape.area());
}
}Finished reading?Mark it done to track your progress.
Tip: use the ← and → keys to move between lessons.