列挙型(enums)

enum キーワードを使用すると、いくつかの異なるバリアントを持つ型を作成できます。

#[derive(Debug)]
enum Direction {
    Left,
    Right,
}

#[derive(Debug)]
enum PlayerMove {
    Pass,                        // 単純なバリアント
    Run(Direction),              // Tuple variant
    Teleport { x: u32, y: u32 }, // 構造体バリアント
}

fn main() {
    let player_move: PlayerMove = PlayerMove::Run(Direction::Left);
    println!("On this turn: {player_move:?}");
}
This slide should take about 5 minutes.

キーポイント:

  • 列挙型を使用すると、1 つの型で一連の値を収集できます。
  • Direction はバリアントを持つ型です。Directionには、Direction::LeftDirection::Right の 2 つの値があります。
  • PlayerMove は、3 つのバリアントを持つ型です。Rust はペイロードに加えて判別式を格納することで、実行時にどのバリアントが PlayerMove 値に含まれているかを把握できるようにします。
  • ここで構造体と列挙型を比較することをおすすめします。
    • どちらでも、フィールドのないシンプルなバージョン(単位構造体)か、さまざまなフィールドがあるバージョン(バリアント ペイロード)を使用できます。
    • 個別の構造体を使用して、列挙型のさまざまなバリアントを実装することもできますが、その場合、それらがすべて列挙型で定義されている場合と同じ型にはなりません。
  • Rust は判別式を保存するために最小限のスペースを使用します。
    • 必要に応じて、必要最小限のサイズの整数を格納します。

    • 許可されたバリアント値がすべてのビットパターンをカバーしていない場合、無効なビットパターンを使用して判別式をエンコードします(「ニッチの最適化」)。たとえば、Option<&u8> には None バリアントに対する整数へのポインタまたは NULL が格納されます。

    • 必要に応じて(たとえば C との互換性を確保するために)判別式を制御できます。

      #[repr(u32)]
      enum Bar {
          A, // 0
          B = 10000,
          C, // 10001
      }
      
      fn main() {
          println!("A: {}", Bar::A as u32);
          println!("B: {}", Bar::B as u32);
          println!("C: {}", Bar::C as u32);
      }

      repr がない場合、10001 は 2 バイトに収まるため、判別式の型には 2 バイトが使用されます。

その他

Rust には、列挙型が占めるスペースを少なくするために使用できる最適化がいくつかあります。

  • null ポインタの最適化: 一部の型で、Rust は size_of::<T>()size_of::<Option<T>>() と等しいことを保証します。

    以下のサンプルコードは、ビット単位の表現が実際にどのようになるかを示しています。コンパイラはこの表現に関して保証しないので、これはまったく安全ではないことに注意してください。

    use std::mem::transmute;
    
    macro_rules! dbg_bits {
        ($e:expr, $bit_type:ty) => {
            println!("- {}: {:#x}", stringify!($e), transmute::<_, $bit_type>($e));
        };
    }
    
    fn main() {
        unsafe {
            println!("bool:");
            dbg_bits!(false, u8);
            dbg_bits!(true, u8);
    
            println!("Option<bool>:");
            dbg_bits!(None::<bool>, u8);
            dbg_bits!(Some(false), u8);
            dbg_bits!(Some(true), u8);
    
            println!("Option<Option<bool>>:");
            dbg_bits!(Some(Some(false)), u8);
            dbg_bits!(Some(Some(true)), u8);
            dbg_bits!(Some(None::<bool>), u8);
            dbg_bits!(None::<Option<bool>>, u8);
    
            println!("Option<&i32>:");
            dbg_bits!(None::<&i32>, usize);
            dbg_bits!(Some(&0i32), usize);
        }
    }