Enumlar

'enum' anahtar sözcüğü birkaç farklı varyanta sahip bir türün yaratılmasına olanak sağlar:

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

#[derive(Debug)]
enum PlayerMove {
    Pass,                        // Basit (simple) varyant
    Run(Direction),              // Demet (Tuple) varyantı
    Teleport { x: u32, y: u32 }, // Yapı (Struct) varyantı
}

fn main() {
    let player_move: PlayerMove = PlayerMove::Run(Direction::Left);
    println!("Sıradaki hamle: {player_move:?}");
}
This slide should take about 5 minutes.

Önemli Noktalar:

  • Numaralandırmalar, bir dizi değeri tek bir tür altında toplamanıza olanak tanır.
  • Direction, varyantları olan bir türdür. Direction'ın iki değeri vardır: Direction::Left ve Direction::Right.
  • PlayerMove üç varyantı olan bir türdür. Rust, varyant değerlerine (variant payloads) ek olarak, çalışma zamanında hangi varyantın PlayerMove değerinde olduğunu bilmesi için bir ayırıcı (discriminant) da depolayacaktır.
  • Bu, yapıları ve numaralandırmaları karşılaştırmak için iyi bir zaman olabilir:
    • Her ikisinde de, alanları olmayan basit bir versiyona (birim yapı) veya farklı alan türlerine sahip bir versiyona (varyant yükleri) sahip olabilirsiniz.
    • Bir enum'ın farklı varyantlarını ayrı yapılarla bile uygulayabilirsiniz ancak o zaman da hepsi bir enum'da tanımlanmış olduğunda olacağı gibi aynı türde olmazlardı.
  • Rust, ayırıcıyı (discriminant) depolamak için minimum alan kullanır.
    • Gerekirse, gereken en küçük boyuttaki bir tam sayıyı depolar

    • İzin verilen varyant değerleri tüm bit desenlerini kapsamıyorsa, ayırıcıyı kodlamak için geçersiz bit desenleri kullanır ("niş/oyuk/niche optimizasyonu"). Örneğin, Option<&u8>, None varyantı için bir tamsayıya gösterici veya NULL depolar.

    • Gerekirse ayırıcıyı kontrol edebilirsiniz (örneğin, C ile uyumluluk için):

      #[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 olmadan, ayırıcı tür 2 baytlık olur, çünkü 10001 2 bayta sığar.

Daha Fazlasını Keşfedin

Rust, enum'ların daha az yer kaplamasını sağlamak için kullanabileceği çeşitli optimizasyonlara sahiptir.

  • Null gösterici optimizasyonu: Bazı türler için, Rust size_of::<T>()'nin size_of::<Option<T>>()'ye eşit olduğunu garanti eder.

    Bitsel gösterimin pratikte nasıl görünebileceğini göstermek istiyorsanız örnek kod aşağıdadır. Derleyicinin bu gösterimle (representation) ilgili hiçbir garanti sağlamadığını, dolayısıyla bunun tamamen emniyetsiz olduğunu belirtmek önemlidir.

    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);
        }
    }