English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

أنواع التجميع Rust

فئة التعداد في Rust ليست بسيطة مثل مفهومها في لغات البرمجة الأخرى، ولكن يمكن استخدامها بسهولة كبيرة:

#[derive(Debug)]

enum Book {
    Papery, Electronic
}

fn main() {
    let book = Book::Papery;
    println!("{:?}", book);
}

نتيجة التنفيذ:

Papery

الكتب مقسومة إلى كتب ورقية (الكتب الورقية) وكتب إلكترونية (الكتب الإلكترونية).

إذا كنت تقوم بتطوير نظام إدارة الكتب الآن، فإنك تحتاج إلى وصف خاصيات الكتب المختلفة (الكتب الورقية تحتوي على رقم الطلب، والكتب الإلكترونية تحتوي فقط على URL)، يمكنك إضافة وصف الخاصيات الكسرية باستخدام التوصيفات الزوجية:

enum Book {
    Papery(u32),
    Electronic(String),
}
let book = Book::Papery(1001);
let ebook = Book::Electronic(String::from("url://..."));

إذا كنت ترغب في تسمية الخاصية، يمكنك استخدام بناء الهيكل:

enum Book {
    Papery { index: u32 },
    Electronic { url: String },
}
let book = Book::Papery{index: 1001};

على الرغم من أن يمكن تسمية بهذا الشكل، ولكن لاحظ أنك لا يمكنك الوصول إلى الخصائص الم绑定 إلى فئة التعداد مثل الوصول إلى حقول الهيكل. طريقة الوصول في بناء الجملة.

جملة match syntax

يهدف الـ enum إلى تصنيف مجموعة معينة من الأشياء، والهدف من التصنيف هو وصف الحالات المختلفة. بناءً على هذا المبدأ، غالبًا ما يتم معالجة الفئات المدرجة بنية الفرع (switch في العديد من اللغات). لغة switch معروفة بشكل كبير، ولكن لا تدعمها Rust، وغالبًا ما ترفض اللغات مثل Java و C# استخدام switch بسبب مشكلة التشابك التي يمكن أن تحدث بسبب نسيان إضافة break، حيث يتم منع هذا النوع من المشاكل من خلال التحقق من الأمان.

يستخدم Rust جملة match لتحقيق بنية الفرع. تعرفوا أولاً كيفية استخدام match لمعالجة الفئات المدرجة:

fn main() {
    enum Book {
        Papery {index: u32},
        Electronic {url: String},
    }
   
    let book = Book::Papery{index: 1001};
    let ebook = Book::Electronic{url: String::from("url...")};
   
    match book {
        Book::Papery { index } => {
            println!("Papery book {}", index);
        },
        Book::Electronic { url } => {
            println!("E-book {}", url);
        }
    }
}

نتيجة التنفيذ:

كتاب Papery 1001

يمكن معاملته block match كتعبير دالة، وهو يمكن أن يكون له عائدات:

match فئة معينة {
    فئة 1 => تعبيرات العائدات
    فئة 2 => تعبيرات العائدات
    ...
}

لكن يجب أن تكون أنواع تعبيرات العائدات متشابهة جميعًا!

إذا تم تعريف خصائص الفئة المدرجة كتوقيع، فسيتم تحديد اسم مؤقت في قالب match:

enum Book {
    Papery(u32),
    Electronic {url: String},
}
let book = Book::Papery(1001);

match book {
    Book::Papery(i) => {
        println!("{}", i);
    },
    Book::Electronic { url } => {
        println!("{}", url);
    }
}

إضافة إلى أن match يمكنه اتخاذ خيار فرعي للفئات المدرجة، يمكنه أيضًا اتخاذ خيار فرعي للبيانات من النوع الصحيح (العدد الصحيح، العدد العشري، الحرف والشريط النصي المقطوع &str). على الرغم من أن استخدام branch selection للنوع العددي العشري هو قانوني، إلا أننا ننصح بعدم القيام بذلك بسبب مشكلة دقة التي قد تؤدي إلى خطأ في الفرع.

عند اتخاذ خيار فرعي للفئات غير المدرجة يجب الانتباه إلى معالجة الاستثناءات، حتى لو لم يكن هناك شيء للقيام به في حالة الاستثناء. يتم تمثيل الاستثناءات باستخدام الأسطر تحت الخط _:

fn main() {
    let t = "abc";
    match t {
        "abc" => println!("نعم"),
        _ => {},
    }
}

فئة Option

Option هي فئة معينة في مكتبة Rust القياسية، وتستخدم هذه الفئة لتعبئة الفراغات التي لا تدعم Rust الاستدلال على القيمة الفارغة null.

يدعم العديد من اللغات وجود القيمة الفارغة null (C/C++، Java)، مما يجعل الأمر سهلاً ولكن يخلق أيضًا مشاكل كبيرة، حتى أن مخترع null اعترف بذلك، "فكرة مريحة تسبب خسائر تصل إلى 10 مليارات دولار".

يؤدي null عادة إلى هجوم قاتل على البرنامج عندما يعتبر المطور كل شيء غير null: لأنه إذا حدث خطأ من هذا النوع، فإن تشغيل البرنامج يجب أن يتوقف تمامًا.

لحل هذه المشكلة، لا تسمح العديد من اللغات بوجود القيمة الفارغة null بشكل افتراضي، ولكن تسمح بوجودها في المستوى اللغوي (غالبًا باستخدام رمز ? في بداية النوع).

يدعم Java القيمة الفارغة null بشكل افتراضي، ولكن يمكن تقييد ظهور null من خلال تضمين @NotNull، وهو حل مؤقت.

يمنع Rust بشكل كامل وجود القيمة الفارغة null في المستوى اللغوي، ولكن لا مفر من أن null يمكن أن يحل بعض المشاكل بشكل فعال، لذا قام Rust بإدخال فئة Option:

enum Option<T> {
    Some(T),
    None,
}

إذا كنت ترغب في تعريف فئة يمكن أن تكون فارغة، يمكنك القيام بذلك:

let opt = Option::Some("مرحبًا");

إذا كنت ترغب في تنفيذ بعض العمليات على opt، يجب عليك أولاً التحقق مما إذا كان هو Option::None:

fn main() {
    let opt = Option::Some("مرحبًا");
    match opt {
        Option::Some(something) => {
            println!("{}", something);
        },
        Option::None => {
            println!("opt هو شيء لا شيء");
        }
    }
}

نتيجة التنفيذ:

مرحبًا

إذا كانت المتغيرات الخاصة بك تبدأ بكونها فارغة، تأخذ في الاعتبار محرر البرمجة، كيف يعرف المحرر ما هو نوع المتغير عندما لا يكون له قيمة؟

لذا يجب أن تكون القيمة الأولية الخاصة بـ Option فارغة واضحة في النوع:

fn main() {
    let opt: Option<&str> = Option::None;
    match opt {
        Option::Some(something) => {
            println!("{}", something);
        },
        Option::None => {
            println!("opt هو شيء لا شيء");
        }
    }
}

نتيجة التنفيذ:

opt هو شيء لا شيء

هذا التصميم يجعل البرمجة باستخدام القيم الفارغة أقل سهولة، ولكن هذا بالضبط ما يحتاجه بناء نظام مستقر وفعال. نظرًا لأن Option يتم إدخاله بشكل افتراضي من قبل محرر Rust، يمكن تضمين None أو Some() دون الحاجة إلى كتابة Option::.

Option هو نوع خاص من أنواع التجميع يمكنه أن يحتوي على فروع تحديد الخيار:

fn main() {
        let t = Some(64);
        match t {
                Some(64) => println!("Yes"),
                _ => println!("No"),
        }
}

جملة if let

let i = 0;
match i {
    0 => println!("zero"),
    _ => {},
}

وضع النتيجة في الدالة الرئيسية:

zero

هدف هذا البرنامج هو التحقق مما إذا كان i هو الرقم 0، وإذا كان كذلك، فإنه يطبع zero.

الآن قصير هذا الكود باستخدام جملة if let:

let i = 0;
if let 0 = i {
    println!("zero");
}

تنسيق جملة if let:

if let قيمة التطابق = المتغير المصدر {
    كتلة الجملة
}

يمكن إضافة كتلة else للتعامل مع الحالات الاستثنائية.

يمكن اعتبار جملة if let "سكر بيتكوين" (سكر بيتكوين هو بديل لجملة match التي تفرق بين حالتين فقط) جملة match.

لأنواع التجميع الكسرية ينطبق عليها ما يلي:

fn main() {
    enum Book {
        Papery(u32),
        Electronic(String)
    }
    let book = Book::Electronic(String::from("url"));
    if let Book::Papery(index) = book {
        println!("Papery {}", index);
    } else {
        println!("Not papery book");
    }
}