7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

enumを使った列挙型

enumを使うと、一つの名前付き列挙型に、複数の型情報を含めた上で、そのうちの一つを選ばせることができます。
列挙型の一つ一つの型情報は互いに異なるものとして扱われます。

Rustの列挙型の特徴として、各要素の型を柔軟に決められる、という点があります。すなわち、ある列挙型のうちある要素は具体的なStringの値を持ち、また別の要素は具体的な(i32, i32)の値を持つ、といったことが可能です(もちろん、何も値を持たせずに要素名だけ定義することも可能です!)。

enum Message {
    Text(String),
    Move { x: i32, y: i32 },
    Quit,
}

そしてそれらは一つの列挙型という同じ型として扱えるので、例えば関数の引数の型として用いることができます(下記のコードは後述するパターンマッチングを使用しています)。

fn print_message(msg: Message) {
    match msg {
        Message::Text(content) => println!("Text: {}", content),
        Message::Move { x, y } => println!("Move to: ({}, {})", x, y),
        Message::Quit => println!("Quit"),
    }
}

列挙型を実際に参照する場合は、列挙型::要素名という記法を用います

fn main() {
    let msg1 = Message::Text(String::from("Hello"));
    let msg2 = Message::Move { x: 10, y: 20 };
    let msg3 = Message::Quit;

    print_message(msg1);
    print_message(msg2);
    print_message(msg3);
}

この機構は非常に興味深いです。TypeScriptには標準のEnumがありますが、正直なところ利用シーンは限られ、実際はオブジェクトリテラルで代用することが多いでしょう。それに比べると、言語の仕様としてこのような柔軟な機構を持つのは(実際に使うかはともかく)特徴的です。

Optionという列挙型

OptionはRustが標準で提供する列挙型です。その実装は以下のようになっています。

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

任意の型Tについて、値が存在していればSomeという要素にその値が格納されています。もし存在していなければOption<T>Noneになります。
Optionを使うメリットは、安全にNullableなデータを表現できることにあります。TOption<T>は明確に違う型なので、Option型の変数を、あたかも値が必ず存在しているように扱うことはできません(コンパイルエラーになります)。この機構により、値の存在有無を確認するコードが強制され、エラーを防ぐことができます。

fn divide(a: i32, b: i32) -> Option<i32> {
    if b == 0 {
        None
    } else {
        Some(a / b)
    }
}

fn main() {
    match divide(10, 2) {
        Some(result) => println!("Result: {}", result),
        None => println!("Cannot divide by zero"),
    }
}

列挙型とパターンマッチング

列挙型とmatchを組み合わせることで、強力なパターンマッチングが可能になります。
matchは列挙型にも対応しており、要素に応じて処理を分けることができます。
列挙型の要素が値を持っている場合、要素名(変数名)のようにしてその値を参照することもできます。

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

fn describe_shape(shape: Shape) {
    match shape {
        Shape::Circle(radius) => println!("Circle with radius {}", radius),
        Shape::Rectangle { width, height } => {
            println!("Rectangle with width {} and height {}", width, height)
        }
        Shape::Triangle(a, b, c) => println!("Triangle with sides {}, {}, {}", a, b, c),
    }
}

fn main() {
    let shape = Shape::Rectangle { width: 10.0, height: 5.0 };
    describe_shape(shape);
}

Optionと組み合わせると、「値が存在するとき」と「そうでないとき」のフローを分けることができます。値が存在するときはSomeに値が保持されているので、Some(変数名)とすることでその値を参照できます。

fn print_number(opt: Option<i32>) {
    match opt {
        Some(value) => println!("Number: {}", value),
        None => println!("No number provided"),
    }
}

fn main() {
    print_number(Some(42));
    print_number(None);
}

さらに、「値が存在し、かつそれが特定の値のとき」のみ処理を実行したい場合は、if let構文が使えます。条件にマッチしたときのみ、その値を変数にバインディングしたうえで処理を実行できるため、matchを使うよりも簡潔に記述できます。

fn main() {
    let opt = Some(42);

    if let Some(42) = opt {
        println!("The answer to life, the universe, and everything!");
    }
}

Optionは強力な型安全システムのように感じます。TypeScriptでも型レベルでundefinednullを含めることができますが、Rustが持つパターンマッチングとの相性の良さはすごいと感じます。

7
0
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
7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?