التعدادات

الكلمة المفتاحية enum تسمح بإنشاء نوع يحتوي على عدة متغيرات مختلفة (variants):

#[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.

نقاط رئيسية (Key Points):

  • التعدادات (Enumerations) تسمح لك بجمع مجموعة من القيم تحت نوع واحد.
  • Direction هو نوع يحتوي على متغيرات (variants). هناك قيمتان لـ Direction: Direction::Left و Direction::Right.
  • PlayerMove هو نوع يحتوي على ثلاثة متغيرات (variants). بالإضافة إلى الحمولات (payloads)، سيخزن Rust مميزًا (discriminant) حتى يعرف في وقت التشغيل (runtime) أي متغير موجود في قيمة PlayerMove.
  • قد يكون هذا وقتًا جيدًا لمقارنة الهياكل (structs) والتعدادات (enums):
    • في كليهما، يمكنك الحصول على نسخة بسيطة بدون حقول (unit struct) أو واحدة تحتوي على أنواع مختلفة من الحقول (variant payloads).
    • يمكنك حتى تنفيذ المتغيرات المختلفة (variants) لتعداد (enum) باستخدام هياكل منفصلة (separate structs) ولكنها لن تكون من نفس النوع كما لو كانت جميعها معرفة في تعداد (enum).
  • Rust يستخدم مساحة صغيرة لتخزين المميز (discriminant).
    • إذا لزم الأمر، فإنه يخزن عددًا صحيحًا (integer) بأصغر حجم مطلوب.

    • إذا لم تغطي القيم المسموح بها للمتغيرات (variant values) جميع أنماط البتات (bit patterns)، فسيستخدم أنماط البتات غير الصالحة (invalid bit patterns) لترميز المميز (discriminant) (تحسين الفجوة (niche optimization)). على سبيل المثال، Option<&u8> يخزن إما مؤشرًا إلى عدد صحيح (integer) أو NULL للمتغير None.

    • يمكنك التحكم في المميز (discriminant) إذا لزم الأمر (على سبيل المثال، للتوافق مع 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، يأخذ نوع المميز (discriminant type) 2 بايت، لأن 10001 يناسب 2 بايت.

المزيد لاستكشافه

لدى Rust العديد من التحسينات (optimizations) التي يمكن استخدامها لجعل التعدادات (enums) تشغل مساحة أقل.

  • تحسين المؤشر الفارغ (Null pointer optimization): بالنسبة لبعض الأنواع (types)، يضمن Rust أن size_of::<T>() يساوي size_of::<Option<T>>().

    مثال إذا كنت تريد إظهار كيف يمكن أن تبدو التمثيلات الثنائية (bitwise representation) في الممارسة العملية. من المهم ملاحظة أن المترجم (compiler) لا يقدم أي ضمانات بخصوص هذا التمثيل، لذلك هذا غير آمن تمامًا.

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