LoginSignup
22
15

More than 5 years have passed since last update.

逆引き Rust 列挙体

Last updated at Posted at 2019-03-23

はじめに

  • Rust の列挙体はデータを持てるというのがユニークですし、パターンマッチングと組み合わせるとパワフルでとても便利です。
  • ここではそんな列挙体の使い方を逆引き形式でまとめてみました。

単純な列挙体を宣言する・生成する・評価する

#[allow(dead_code)]
fn main() {

    // 単純な enum の定義
    enum E1 {
        A,
        B,
        C,
    }

    // enum の値を生成
    let e1 = E1::B;

    // match 式で enum を評価する
    let result = match e1 {
        E1::A => 1,
        E1::B => 2,
        E1::C => 3,
    };
    assert_eq!(result, 2);

    // if let 式で enum を評価する
    let e1 = E1::C;
    let result = if let E1::C = e1 {
        true
    } else {
        false
    };
    assert!(result);
}

データを持つ列挙体を宣言する・生成する・評価する

#[allow(dead_code)]
fn main() {

    // 複雑な enum の定義
    enum E2 {
        // データを持たないヴァリアント
        N,
        // 名前なしのデータを持つヴァリアント
        T(i32),
        // 名前付きデータを持つヴァリアント
        S{x: i32, y:i32},
    }

    // 名前なしデータを持つヴァリアントの初期化
    let e2 = E2::T(-1);

    // match 式で名前なしデータを持つヴァリアントを評価する
    let result = match e2 {
        E2::N => 1,
        E2::T(n) => n * 2,
        E2::S {x: _, y: _} => 3,
    };
    assert_eq!(result, -2);

    // if let 式で名前なしデータを持つヴァリアントを評価する
    let e2 = E2::T(10);
    let result = if let E2::T(n) = e2 { n } else { 0 };
    assert_eq!(result, 10);
}

列挙体の比較をする

  • PartialOrdPartialEq を使うとヴァリアント同士での比較ができるようになります。
  • データ付きヴァリアント同士が持っているデータに関係なく同じであるかどうか判定したい場合は std::mem::discriminant を使います。
#[allow(dead_code)]
fn main() {

    // PartialOrd, PartialEq で enum のヴァリアント同士を比較できるようになる
    #[derive(PartialOrd, PartialEq, Debug)]
    enum E3 {
        N1,
        N2,
        T1(i32),
        T2(i32),
        S1{x: i32, y: i32},
        S2{x: i32, y: i32},
    }

    // 同じ値かどうか比較できる
    assert_eq!(E3::N1, E3::N1);
    assert_ne!(E3::N1, E3::N2);

    // enum のヴァリアント同士での大小を比較できる
    assert!(E3::N1 < E3::N2);

    // 名前なしデータを持つヴァリアント同士の比較
    assert_eq!(E3::T1(10), E3::T1(10), "同じヴァリアントで同じデータなら同じ値");
    assert_ne!(E3::T1(10), E3::T1(20), "同じヴァリアントで違うデータなら違う値");
    assert_ne!(E3::T1(10), E3::T2(10), "違うヴァリアントで同じデータなら違う値");

    // 名前なしデータを持つヴァリアント同士の比較はデータに関係なくヴァリアントの宣言順
    assert!(E3::T1(1) < E3::T2(1));
    assert!(E3::T1(2) < E3::T2(1));
    assert!(E3::T1(1) < E3::T2(2));

    // 名前付きデータを持つヴァリアント同士の比較
    assert_eq!(E3::S1{x:10, y:20}, E3::S1{x:10, y:20}, "同じヴァリアントで同じデータなら同じ値");
    assert_ne!(E3::S1{x:10, y:20}, E3::S1{x:10, y:30}, "同じヴァリアントで違うデータなら違う値");
    assert_ne!(E3::S1{x:10, y:20}, E3::S2{x:10, y:20}, "違うヴァリアントで同じデータなら違う値");

    // 異なる形式のヴァリアント同士でも宣言順で大小を比較できる
    assert!(E3::N1 < E3::T1(1));

    // データを持つヴァリアントでデータを無視して同じヴァリアントかどうかを判定する
    use std::mem;
    assert_eq!(mem::discriminant(&E3::T1(100)), mem::discriminant(&E3::T1(200)), "同じヴァリアント違うデータで同値判定");
    assert_ne!(mem::discriminant(&E3::T1(100)), mem::discriminant(&E3::T2(100)), "違うヴァリアント同じデータで異値判定");
}

列挙体から文字列へ

  • enum で std::fmt::Display を実装すると文字列への変換ができます
#[allow(dead_code)]
fn main() {

    enum E4 {
        A,
        B(i32),
        C{n: i32, m: i32}
    }

    // std::fmt::Display を実装すると文字列表現が定義できる
    use std::fmt;
    impl fmt::Display for E4 {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            match *self {
                E4::A => write!(f, "[E4::A]"),
                E4::B(n) => write!(f, "[E4::B({})]", n),
                E4::C{n: nn, m: mm} => write!(f, "[E4::C{{n:{},m:{}}}]", nn, mm),
            }
        }
    }

    assert_eq!(format!("{}", E4::A), "[E4::A]", "format! で意図したとおりに出力できる");
    assert_eq!(E4::B(10).to_string(), "[E4::B(10)]", "to_string() でも同様の文字列が取得できる");
    assert_eq!(E4::C{n: 1, m: 2}.to_string(), "[E4::C{n:1,m:2}]", "データも正常に反映される");
}

文字列から列挙体へ

  • enum で std::str::FromStr を実装すると文字列からの変換ができます
#[allow(dead_code)]
fn main() {

    #[derive(PartialEq, Debug)]
    enum E5 {
        A,
        B,
        C
    }

    use std::str::FromStr;
    impl FromStr for E5 {
        type Err = ();
        fn from_str(s: &str) -> Result<Self, Self::Err> {
            match s {
                "A" => Ok(E5::A),
                "B" => Ok(E5::B),
                "C" => Ok(E5::C),
                _ => Err(())
            }
        }
    }

    assert_eq!(E5::from_str("A"), Ok(E5::A), "from_str で文字列から enum に");
    assert_eq!(E5::from_str("B"), Ok(E5::B), "from_str で文字列から enum に");
    assert_eq!("C".parse::<E5>(), Ok(E5::C), "from_str が定義されていると parse も使える");
}
22
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
15