Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

مراجعة التعداد Option

جزء من قسم Object Oriented Programming في رحلة Rust على Coddy — الدرس 17 من 61.

لقد واجهت بالفعل Option<T> في لغة Rust—وهو الـ enum القياسي لتمثيل القيم التي قد تكون موجودة أو غير موجودة. الآن دعونا ننظر إليه من منظور البرمجة كائنية التوجه (OOP): الـ Option ليس مجرد enum، بل هو نوع يحتوي على دوال مدمجة قوية تتيح لك التعامل مع القيم التي قد تكون فارغة (nullable) بأمان وأناقة.

بدلاً من المطابقة في كل مرة، يمكنك استخدام الدوال (methods) مباشرة على قيم Option. تعيد الدالة unwrap_or القيمة الداخلية إذا كانت موجودة، أو قيمة افتراضية توفرها:

let maybe_name: Option<String> = Some(String::from("Alice"));
let name = maybe_name.unwrap_or(String::from("Guest"));
println!("{}", name);  // Alice

let empty: Option<String> = None;
let fallback = empty.unwrap_or(String::from("Guest"));
println!("{}", fallback);  // Guest

تقوم الطريقة map بتحويل القيمة الداخلية إذا كانت موجودة، مع ترك None دون تغيير. يتيح لك ذلك ربط العمليات ببعضها البعض دون الحاجة إلى التحقق باستمرار من None:

let maybe_num: Option<i32> = Some(5);
let doubled = maybe_num.map(|x| x * 2);
println!("{:?}", doubled);  // Some(10)

let nothing: Option<i32> = None;
let still_nothing = nothing.map(|x| x * 2);
println!("{:?}", still_nothing);  // None

تجسد هذه الطرق مبادئ البرمجة كائنية التوجه (OOP) - حيث يغلف النوع Option مفهوم "ربما قيمة" ويوفر واجهة نظيفة للتعامل معها. أنت تستدعي الطرق (methods) على الكائن بدلاً من كتابة منطق خارجي لفحصه.

challenge icon

التحدي

سهل

لنقم ببناء نظام لملفات تعريف المستخدمين يتعامل بسلاسة مع البيانات الاختيارية باستخدام طرق Option. قد يكون لدى المستخدمين اسم مستعار (nickname) محدد، أو قد لا يكون لديهم - وسيتعامل نظامك مع كلتا الحالتين بأناقة دون الحاجة إلى مطابقة صريحة (explicit matching).

ستقوم بإنشاء ملفين لتنظيم الكود الخاص بك:

  • profile.rs: قم بتعريف هيكل Profile عام مع حقلين: username (String) و nickname (Option<String>). قم بتنفيذ ما يلي:
    • منشئ new يأخذ اسم مستخدم واسم مستعار اختياري.
    • طريقة display_name تعيد الاسم المستعار إذا كان موجوداً، أو اسم المستخدم كبديل - استخدم unwrap_or للتعامل مع هذا.
    • طريقة formatted_nickname تعيد Option<String> مع وضع الاسم المستعار بين أقواس (مثل "[CoolNick]") إذا كان موجوداً، أو None إذا لم يكن هناك اسم مستعار - استخدم map لتحويل القيمة.
  • main.rs: قم باستيراد وحدة الملف الشخصي (profile module) وأنشئ ملفين شخصيين - أحدهما باسم مستعار والآخر بدونه. قم باستعراض كلتا الطريقتين على كل ملف شخصي لتوضيح كيف تتعامل طرق Option مع القيم الموجودة والغائبة بشكل مختلف.

بالنسبة لطريقة formatted_nickname، استخدم تنسيق التصحيح ({:?}) عند الطباعة لأنها تعيد Option.

يجب أن يتبع مخرجك هذا التنسيق تماماً، مع إظهار كلا الملفين الشخصيين:

Display name: {name}
Formatted nickname: {option}
Display name: {name}
Formatted nickname: {option}

على سبيل المثال، مع ملف شخصي لاسم المستخدم "alice" مع الاسم المستعار "Ace"، وآخر لاسم المستخدم "bob" بدون اسم مستعار، سيكون المخرج:

Display name: Ace
Formatted nickname: Some("[Ace]")
Display name: bob
Formatted nickname: None

ستتلقى ثلاثة مدخلات: اسم المستخدم الأول، والاسم المستعار للمستخدم الأول، واسم المستخدم الثاني (الذي ليس لديه اسم مستعار).

ورقة مرجعية

يوفر النوع Option<T> طرقاً للتعامل مع القيم الاختيارية دون الحاجة إلى المطابقة الصريحة (explicit matching).

تعيد الطريقة unwrap_or القيمة الداخلية إذا كانت موجودة، أو قيمة افتراضية إذا كانت القيمة هي None:

let maybe_name: Option<String> = Some(String::from("Alice"));
let name = maybe_name.unwrap_or(String::from("Guest"));
println!("{}", name);  // Alice

let empty: Option<String> = None;
let fallback = empty.unwrap_or(String::from("Guest"));
println!("{}", fallback);  // Guest

تقوم الطريقة map بتحويل القيمة الداخلية إذا كانت موجودة، مع ترك None دون تغيير:

let maybe_num: Option<i32> = Some(5);
let doubled = maybe_num.map(|x| x * 2);
println!("{:?}", doubled);  // Some(10)

let nothing: Option<i32> = None;
let still_nothing = nothing.map(|x| x * 2);
println!("{:?}", still_nothing);  // None

تسمح هذه الطرق بربط العمليات (chaining) على قيم Option دون الحاجة المستمرة للتحقق من وجود None.

جرّب بنفسك

mod profile;

use profile::Profile;

fn main() {
    // قراءة المدخلات
    let mut input1 = String::new();
    std::io::stdin().read_line(&mut input1).expect("Failed to read line");
    let username1 = input1.trim().to_string();

    let mut input2 = String::new();
    std::io::stdin().read_line(&mut input2).expect("Failed to read line");
    let nickname1 = input2.trim().to_string();

    let mut input3 = String::new();
    std::io::stdin().read_line(&mut input3).expect("Failed to read line");
    let username2 = input3.trim().to_string();

    // TODO: إنشاء الملف الشخصي الأول باستخدام username1 و nickname1 (Some)
    
    // TODO: إنشاء الملف الشخصي الثاني باستخدام username2 وبدون لقب (None)
    
    // TODO: طباعة display_name و formatted_nickname للملف الشخصي الأول
    // استخدم {:?} لـ formatted_nickname لأنه يعيد Option
    
    // TODO: طباعة display_name و formatted_nickname للملف الشخصي الثاني
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming