teru_0x01.log

技術メモと雑記

Rustにっき/11日目・列挙型

11日目です。文法もちょくちょくやらないとソースが読めないので列挙型の話です。

列挙型

enumとも言います。他の言語でも名前付き定数の集合を定義するためによく使われています。 例えば信号機型ならこんな感じ

enum Signal {
    Red,
    Yellow,
    Green
}

Red, Yellow, Greenはvariantもしくは構成子と言います。

識別子には定数値を指定できます。

enum HttpStatus {
    Ok = 200,
    Created = 201,
    NotFound = 404,
}

指定しないと0から順に割り振られるので、信号機の例だとSignal::Redは0, Yellowは1のようになります。

列挙型の構成子から整数型へキャストできますが、その逆はできません。

let a = Signal::Red as i32;
assert_eq!(a, 0);

構造体のようにメソッドも書くことができます。

enum HttpStatus {
    Ok = 200,
    Created = 201,
    NotFound = 404,
}

impl HttpStatus {
    fn did_succeeded(code: HttpStatus) -> bool {
        let code = code as i32;
        return 200 <= code && code < 300;
    }
}

tuple variants

struct Image {
    size: ImageSize,
    url: String
}

enum ImageSize {
    Big(usize, usize),
    Small(usize, usize)
}

ImageSizeの構成子は引数を持っており、これらの構成子はtuple variantsと呼ばれます。タプル型構造体を構成子として持つような感じでしょうか。

let big_image = Image{size: ImageSize::Big(2000, 3000), url: "http://hogebar/image.jpg".to_string()};

コンストラクタで値を渡すことにより様々な構成子の値を持つImageSizeを作成できます。

struct variants

構造体自体も構成子として持つことができます。

enum Figure {
    Circle { center: Point2d, radius: f32 },
    Triangle { corner1: Point2d, corner2: Point2d, corner3: Point2d }
}

Option, Result

そういえば今までよく使ってきたOption<T>Result<T, E>enum型でした。TやEのような型変数をもつenumもあります。これらの構成子となっているSome(T)Ok(T) などは先ほどのtuple variantsです。

enum Option<T> { 
    None,
    Some(T) 
}

enum Result<T, E> {
    Ok(T),
    Err(E)
}

値を取り出す

列挙型の中のデータにアクセスするには、パターンマッチを利用します。 先ほどのImageの例を使います。

struct Image {
    size: ImageSize,
    url: String
}

enum ImageSize {
    Big(usize, usize),
    Small(usize, usize)
}

fn main() {
    let mut big_image = Image{size: ImageSize::Big(2000, 3000), url: "http://hogebar/image.jpg".to_string()};
}

画像サイズを説明する文字列を返す機能が欲しければ以下のようなメソッドを追加します。パターンマッチする部分をwidth, heightのようなローカル変数に代入して取り出しています。

impl Image {
    fn get_size_description(&self) -> String {
        match self.size {
            ImageSize::Big(width, height) => format!("Big size => width: {}, height: {}", width, height),
            ImageSize::Small(width, height) => format!("Small size => width: {}, height: {}", width, height)
        }
    }
}

fn main() {
   let mut big_image = Image{size: ImageSize::Big(2000, 3000), url: "http://hogebar/image.jpg".to_string()};
   
   println!("{}", big_image.get_size_description());
}

実行結果

Big size => width: 2000, height: 3000

参照を取り出す

match result {
    Ok(ref mut val) => {
        handle(val); // &mut を渡す
    },
    Err(ref err) => log_error(err),
}

範囲を取り出す

..= を使う.範囲式の..が右開区間であるのに対し,閉区間になる...はまだ実験的にしか使えないらしい.

match c {
    '0' ..= '9' | 'a' ..= 'z' => {
        // process
    },
    _ => process(),
}

パターンガード

matchの分岐にifを書くことができる.

    match val {
        Some(m) if m < 15 => {
            println!("true");
        },
        _ => println!("false")
    }

マッチ全体を取り出す

@パターンを使う

    let n = Some(2);
    match n {
        l @ Some(_) => {
            println!("true: {:?}", l);
        },
        _ => println!("false")
    }

構成子を追加したい

enumの定義を変更するしか無いようです。 そのenumに対してマッチするmatch式を全て変更する必要があります。