Menu
Coddy logo textTech

וקטורים של Traits

חלק מהיחידה תכנות מונחה עצמים במסלול ה-Rust של Coddy. שיעור 46 מתוך 61.

ראית איך Box<dyn Trait> מאפשר למשתנה יחיד להכיל טיפוסים שונים. הכוח האמיתי מתגלה כשמשלבים זאת עם אוספים: אחסון של כמה טיפוסים שונים בווקטור יחיד.

וקטור רגיל דורש שכל האיברים יהיו מאותו סוג. אי אפשר ליצור Vec<Dog> שמכיל גם חתולים. אבל בעזרת אובייקטי trait אפשר ליצור וקטור שמכיל כל דבר שמממש trait משותף:

trait Speak {
    fn speak(&self) -> String;
}

struct Dog;
struct Cat;
struct Bird;

impl Speak for Dog {
    fn speak(&self) -> String { String::from("Woof!") }
}

impl Speak for Cat {
    fn speak(&self) -> String { String::from("Meow!") }
}

impl Speak for Bird {
    fn speak(&self) -> String { String::from("Tweet!") }
}

fn main() {
    let animals: Vec<Box<dyn Speak>> = vec![
        Box::new(Dog),
        Box::new(Cat),
        Box::new(Bird),
    ];
}

הטיפוס Vec<Box<dyn Speak>> פירושו „וקטור של אובייקטי trait באריזה”. כל איבר הוא Box שמצביע על משהו שמממש את Speak: הטיפוסים בפועל יכולים להיות שונים. עוטפים כל ערך ב־Box::new() כשמוסיפים אותו לווקטור.

התבנית הזו חיונית לבניית מערכות גמישות שבהן צריך לנהל אוספים של אובייקטים קשורים אך שונים, כמו רכיבי ממשק משתמש, ישויות במשחק או מערכות תוספים.

challenge icon

אתגר

קל

בוא נבנה מערכת התראות שיכולה לטפל בסוגים שונים של התראות! תיצור אוסף שמאחסן מגוון סוגי התראות — הודעות אימייל, הודעות SMS והודעות דחיפה — כולן בווקטור יחיד באמצעות אובייקטי trait.

תארגן את הקוד שלך בשני קבצים:

  • notifications.rs: הגדר trait ציבורי בשם Notify עם מתודה בשם send שמקבלת את &self ומחזירה String שמתאר את ההתראה שנשלחת. לאחר מכן צור שלושה מבנים ציבוריים:
    • Email: עם שדה ציבורי בשם recipient מסוג (String). המתודה send שלו צריכה להחזיר Email to: {recipient}
    • Sms: עם שדה ציבורי בשם phone מסוג (String). המתודה send שלו צריכה להחזיר SMS to: {phone}
    • Push: עם שדה ציבורי בשם device מסוג (String). המתודה send שלו צריכה להחזיר Push to: {device}
  • main.rs: כלול את מודול ההתראות שלך וצור וקטור מסוג Vec<Box<dyn Notify>> שמכיל את כל שלושת סוגי ההתראות. השתמש בקלטים שסופקו כדי ליצור התראה אחת מכל סוג, הוסף אותן לווקטור לפי הסדר (Email, Sms, Push), ואז הדפס את התוצאה של קריאה ל-send() על האיבר הראשון בווקטור.

הרעיון המרכזי כאן הוא שלמרות ש-Email, Sms ו-Push הם מבנים שונים לחלוטין, כולם יכולים להימצא יחד באותו וקטור מפני שהם חולקים את ה-trait Notify. כל איבר עטוף באמצעות Box::new() כדי ליצור אובייקט trait.

הפלט שלך צריך להציג את ההתראה הראשונה:

Email to: {recipient}

לדוגמה, עם הקלטים alice@example.com, 555-1234 ו-iPhone-12:

Email to: alice@example.com

יתקבלו שלושה קלטים: כתובת האימייל של הנמען, מספר הטלפון ושם המכשיר.

נסו בעצמכם

mod notifications;

use notifications::{Notify, Email, Sms, Push};

fn main() {
    // קרא את הקלטים
    let mut recipient = String::new();
    std::io::stdin().read_line(&mut recipient).expect("Failed to read line");
    let recipient = recipient.trim().to_string();

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

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

    // TODO: צור Vec<Box<dyn Notify>> שיכיל סוגים שונים של התראות
    // TODO: צור מופעים של Email, Sms ו-Push באמצעות הקלטים
    // TODO: הוסף אותם לווקטור לפי הסדר (Email, Sms, Push) באמצעות Box::new()
    // TODO: הדפס את התוצאה של קריאה ל-send() על האיבר הראשון
}
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר Rust אונליין