Defining an Enum - The Rust Programming Language
構造体は関連するフィールドとデータをグループ化する1つの方法である。
列挙型(enum)は取りうる値のセットを定義する。
enum IpAddrKind { V4, V6, }
Enumの値
let four = IpAddrKind::V4; let six = IpAddrKind::V6;
enumには直接データを持たせることができ、さらに値ごとに異なるデータを関連付けることが可能
enum IpAddr { V4(u8, u8, u8, u8), V6(String), } let home = IpAddr::V4(127, 0, 0, 1); let loopback = IpAddr::V6(String::from("::1"));
標準ライブラリのIpAddrは、それぞれの値に異なる構造体を持たせている。
enumには様々なデータをもたせることが可能である。
enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), }
- Qute:データなし
- Move:構造体のような名前付きフィールド
- Write:単一のString型
- ChangeColor:3つの整数型
上記を構造体で表現した場合以下のようになるが、これらの全ての型に対応するような関数を定義することは簡単ではない。
struct QuitMessage; // unit struct struct MoveMessage { x: i32, y: i32, } struct WriteMessage(String); // tuple struct struct ChangeColorMessage(i32, i32, i32); // tuple struct
また、enumもメソッドを定義することができる。
impl Message { fn call(&self) { // method body would be defined here } } let m = Message::Write(String::from("hello")); m.call();
Option型とNull値に対する優位性
Option型は標準ライブラリで定義されているenumで、何某かの値を持つか、何も無いかを一般化する。
Rustは他の多くの言語が持っているNullがなく、nullかnull以外かをOption<T>
でコード化する。
enum Option<T> { None, Some(T), }
Option<T>
は通常のenumであり、Some(T)
とNone
もOption<T>
の取りうる値である。
<T>
記法は、ジェネリック型引数で、今はSome
があらゆる型のデータを1つ保持することができ、T
マークはOption型全体として、実際の型に置き換わるとだけ理解しておけば良い。
let some_number = Some(5); let some_char = Some('e'); let absent_number: Option<i32> = None;
some_number
はOption<i32>
型で、some_char
はOption<char>
型で異なる型になる。- Rustは
Some
に渡された実際の値で型を推測できるが、absent_number
は推測できないため、型を指定する必要がある Some
の場合は値があることを、None
の場合は値がないことを表す。
何がnullよりよいのか?
Option<T>
とT
は異なる型になり、コンパイラはOption<T>
の値を確実に有効な値としては使用しない。
// i8とOption<i8>は異なる型のため、加算できないのでコンパイルエラーになる let x: i8 = 5; let y: Option<i8> = Some(5); let sum = x + y;
Rustではi8
のような型の場合、コンパイラーが有効な値であることを保証してくれるため、nullチェックの必要がない。
Option<i8>
のような型の場合のみ、値がない場合の考慮が必要で、そのようなケースをハンドリングしているかどうかをコンパイラーは確認する。
言い換えると、T
型の処理をするには、Option<T>
をT
に変換する必要があり、その時、値がない場合のケースの処理も強制させるため、not nullエラーが発生しない。