🦀

Rustの勉強 6日目

2022/10/02に公開

The Rust Programming Language 日本語版 第6章

列挙型とパターンマッチについて

6.1 Enumを定義する

  • 列挙型は enum キーワードで宣言する
  • 列挙型の列挙子は 型名::値名 で記述する
enum IpAddrKind {
    V4,
    V6,
}

let four = IpAddrKind::V4;
  • 列挙子には直接データを格納することも可能。各列挙子で異なるデータ型を格納しても良い。
enum IpAddrKind {
    V4(String),
    V6(String),
}

let four = IpAddrKind(String::from("127.0.0.1"));
  • Enumもメソッドが持てて構造体と同様に impl で実装する
  • Enumは直和型を実現する用途として使われている
    • Option 型はプレリュードにすら採用されている
    • Option 型から値を取り出すためのメソッドがたくさん用意されている

6.2 match制御フロー演算子

  • Enum型の列挙子に応じて処理をする場合にmatchが使える
  • 列挙子が値を持っている場合には、変数をパターンに追加する
    • これも OCaml とかのパターンマッチに似てる
enum Coin{
    Penny,
    Nickel,
    Dime,
    Quater(UsState),
}

fn value_in_cents(coin: Coin) -> u32 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter(state) => { // state で UsState の値を保持
            println!("State quarter from {:?}!", state);
            25
        },
    }
}
  • Enum型をmatchするときはすべての列挙子に関してパターンを書かないといけない
    • デフォルトのパターンを指定するために _ というプレースホルダが使える。(これも OCaml に似てる)

6.3 if letで簡潔な制御フロー

  • if let は特定のケースだけ処理したい場合の match の糖衣構文のようなもの
let mut count = 0;
if let Coin::Quarter(state) = coin {
    println!("State quarter from {:?}", state);
} else {
    count += 1
}

Rustlings

enums の一部がそれっぽいのでやってみる

enums1.rs

main の中でアクセスされている型を定義して終わり

#[derive(Debug)]
enum Message {
    Quit,
    Echo,
    Move,
    ChangeColor,
}

fn main() {
    println!("{:?}", Message::Quit);
    println!("{:?}", Message::Echo);
    println!("{:?}", Message::Move);
    println!("{:?}", Message::ChangeColor);
}

enums2.rs

やはり enums1.rs と同様に、 mainmessages 内で呼ばれているような値を持つように Message の定義を埋めただけ。

#[derive(Debug)]
enum Message {
    Move { x: i32, y: i32 },
    Echo(String),
    ChangeColor(i32, i32, i32),
    Quit,
}

impl Message {
    fn call(&self) {
        println!("{:?}", &self);
    }
}

fn main() {
    let messages = [
        Message::Move { x: 10, y: 30 },
        Message::Echo(String::from("hello world")),
        Message::ChangeColor(200, 255, 255),
        Message::Quit,
    ];

    for message in &messages {
        message.call();
    }
}

enums3.rs

テストで用意されていた処理と State の定義をもとに Message の列挙子の値の型を合わせたら終わった。

enum Message {
    ChangeColor((u8, u8, u8)),
    Echo(String),
    Move(Point),
    Quit,
}

struct Point {
    x: u8,
    y: u8,
}

struct State {
    color: (u8, u8, u8),
    position: Point,
    quit: bool,
}

impl State {
    fn change_color(&mut self, color: (u8, u8, u8)) {
        self.color = color;
    }

    fn quit(&mut self) {
        self.quit = true;
    }

    fn echo(&self, s: String) {
        println!("{}", s);
    }

    fn move_position(&mut self, p: Point) {
        self.position = p;
    }

    fn process(&mut self, message: Message) {
        match message {
            Message::ChangeColor(c) => self.change_color(c),
            Message::Echo(s) => self.echo(s),
            Message::Move(p) => self.move_position(p),
            Message::Quit => self.quit(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_match_message_call() {
        let mut state = State {
            quit: false,
            position: Point { x: 0, y: 0 },
            color: (0, 0, 0),
        };
        state.process(Message::ChangeColor((255, 0, 255)));
        state.process(Message::Echo(String::from("hello world")));
        state.process(Message::Move(Point { x: 10, y: 15 }));
        state.process(Message::Quit);

        assert_eq!(state.color, (255, 0, 255));
        assert_eq!(state.position.x, 10);
        assert_eq!(state.position.y, 15);
        assert_eq!(state.quit, true);
    }
}

Discussion