Enums

کلمه کلیدی enum اجازه ایجاد نوع داده‌ای را می دهد که دارای چندین گونه مختلف است:

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

#[derive(Debug)]
enum PlayerMove {
    Pass,                        // Simple variant
    Run(Direction),              // Tuple variant
    Teleport { x: u32, y: u32 }, // Struct variant
}

fn main() {
    let m: PlayerMove = PlayerMove::Run(Direction::Left);
    println!("در این پیچ: {:?}", m);
}
This slide should take about 5 minutes.

نکات کلیدی:

  • Enumها به شما امکان می دهند مجموعه‌ای از مقادیر مختلف با نوع‌های مختلف را تحت یک نوع جمع آوری کنید.
  • Direction یک type با گونه‌های مختلف است. دو مقدار Direction وجود دارد: Direction::Left و Direction::Right.
  • PlayerMove is a type with three variants. In addition to the payloads, Rust will store a discriminant so that it knows at runtime which variant is in a PlayerMove value.
  • الان زمان خوبی برای مقایسه ساختارها و Enumهاست:
    • در هر دو، می توانید یک نسخه ساده بدون فیلد (unit struct) یا یکی با انواع مختلف فیلد (variant payloads) داشته باشید.
    • شما حتی می توانید انواع مختلف یک Enum را با ساختارهای جداگانه پیاده سازی کنید، اما در آن صورت آنها از همان نوعی که در ابتدا تعریف کردید یعنی Enum نخواهند بود.
  • Rust از حداقل فضا برای ذخیره‌سازی متمایز‌کننده (discriminant) استفاده می‌کند.
    • در صورت لزوم، یک عدد صحیح با کوچکترین اندازه مورد نیاز را ذخیره می‌کند

    • اگر مقادیر متغیر مجاز همه الگوهای bit را پوشش ندهند، از الگوهای bit نامعتبر برای رمزگذاری متمایز کننده (یک "niche optimization") استفاده می‌کند. برای مثال، Option<&u8> یک اشاره‌گر به یک عدد صحیح یا NULL را برای نوع None ذخیره می‌کند.

    • شما می توانید در صورت نیاز (به عنوان مثال، برای سازگاری با C) discriminant را کنترل کنید:

      #[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، نوع discriminant دو بایت حافظه اشغال میکند، زیرا 10001 در 2 بایت جا می‌شود.

برای کاوش بیشتر

زبان Rust دارای چندین بهینه‌سازی دارد که می‌تواند برای کاهش فضای اشغال شده توسطEnumها استفاده کند.

  • بهینه‌سازی اشاره‌گر 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);
        }
    }