RustArena

Step 6 of 7

Methods on enums and matches!

Enums get impl blocks just like structs, and the method body is usually a match self. Inside the impl, Self::Circle is shorthand for Shape::Circle.

enum Shape {
    Circle(f64),
    Rectangle { width: f64, height: f64 },
}

impl Shape {
    fn area(&self) -> f64 {
        match self {
            Self::Circle(r) => std::f64::consts::PI * r * r,
            Self::Rectangle { width, height } => width * height,
        }
    }

    fn scaled(&self, factor: f64) -> Shape {
        match self {
            Self::Circle(r) => Self::Circle(r * factor),
            Self::Rectangle { width, height } => Self::Rectangle {
                width: width * factor,
                height: height * factor,
            },
        }
    }

    fn is_round(&self) -> bool {
        matches!(self, Self::Circle(_))
    }
}

fn main() {
    let shapes = [Shape::Circle(1.0), Shape::Rectangle { width: 2.0, height: 3.0 }];
    for s in &shapes {
        let big = s.scaled(2.0);
        println!("area {:.2}, doubled {:.2}, round: {}", s.area(), big.area(), s.is_round());
    }
}
area 3.14, doubled 12.57, round: true
area 6.00, doubled 24.00, round: false

matches!

matches!(value, pattern) is true if the value fits the pattern, false otherwise. It's a short form of a match with a => true arm and a _ => false arm, and it's ideal for "is it this kind?" questions, which come up constantly in if conditions:

if matches!(light, Light::Red) { /* stop */ }

Small enums: derive Copy

An enum whose variants hold no data (or only Copy data) can derive Clone and Copy. Then methods can take self by value and the original stays usable, which suits state machines: light = light.next();.

#[derive(Debug, Clone, Copy)]
enum Light {
    Red,
    Green,
    Yellow,
}

impl Light {
    fn next(self) -> Self {
        match self {
            Self::Red => Self::Green,
            Self::Green => Self::Yellow,
            Self::Yellow => Self::Red,
        }
    }

    fn can_go(self) -> bool {
        matches!(self, Self::Green)
    }
}

fn main() {
    let mut light = Light::Red;
    for _ in 0..3 {
        light = light.next();
        println!("{:?}: go = {}", light, light.can_go());
    }
}
Green: go = true
Yellow: go = false
Red: go = false

Associated functions work on enums too. A common one turns outside input into a variant, returning an Option because the input may not name any variant: fn from_char(c: char) -> Option<Self>. You'll write one in the last challenge of this step.

Your task

A game map is made of cells. Write three methods on Cell:

Challenge 2

Give Direction some methods. It derives Copy, so the methods take self by value:

Challenge 3

Now control a robot with a string of letters. Write:

for c in path.chars() walks the characters, and let...else handles the bad ones neatly.

Previous: if let and let...else Next: Modeling with enums: parsing commands