שליחה דינמית
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Rust של Coddy. שיעור 45 מתוך 61.
כשמשתמשים בגנריות עם חסמי תכונה כמו <T: Summary>, Rust קובעת את הסוג המדויק בזמן הקומפילציה. זה נקרא שליחה סטטית: המהדר מייצר קוד מותאם לכל סוג קונקרטי שבו משתמשים. זה מהיר, אבל יש מגבלה: משתנה יכול להכיל רק סוג מסוים אחד.
מה אם תצטרכו משתנה יחיד שיוכל להכיל סוגים שונים בזמן ריצה? כאן נכנסים לתמונה אובייקטי תכונה. באמצעות Box<dyn Trait>, תוכלו לאחסן כל סוג שמממש את התכונה:
trait Speak {
fn speak(&self) -> String;
}
struct Dog;
struct Cat;
impl Speak for Dog {
fn speak(&self) -> String { String::from("Woof!") }
}
impl Speak for Cat {
fn speak(&self) -> String { String::from("Meow!") }
}
fn main() {
let animal: Box<dyn Speak> = Box::new(Dog);
println!("{}", animal.speak()); // "Woof!"
let animal: Box<dyn Speak> = Box::new(Cat);
println!("{}", animal.speak()); // "Meow!"
}
מילת המפתח dyn מציינת שליחה דינמית: Rust מחפשת בזמן ריצה איזו מתודה לקרוא, במקום בזמן הידור. ה־Box נחוץ משום שלאובייקטים של תכונה אין גודל ידוע; Box מספק מצביע בגודל קבוע.
חשוב על זה כך: גנריות אומרות "אני עובד עם טיפוס T", בעוד שאובייקטי trait אומרים "אני עובד עם כל דבר שיכול לעשות זאת." הפשרה היא עלות קטנה בזמן הריצה בתמורה לגמישות של טיפול בטיפוסים שונים באמצעות אותו משתנה.
אתגר
קלבואו נבנה מערכת להשכרת כלי רכב שמדגימה את העוצמה של שליחה דינמית! תיצרו trait שמגדיר מה כל כלי רכב שניתן להשכיר יכול לעשות, ואז תשתמשו ב-Box<dyn Trait> כדי לאחסן סוגים שונים של כלי רכב באותו משתנה.
תארגנו את הקוד שלכם בשני קבצים:
vehicle.rs: הגדירו trait ציבורי בשםRentableעם מתודה בשםrental_infoשמקבלת&selfומחזירהString. לאחר מכן צרו שתי struct-ים ציבוריים:Car: עם שדה ציבורי בשםmodel(String). המתודהrental_infoשלה צריכה להחזירCar: {model}Bike: עם שדה ציבורי בשםbrand(String). המתודהrental_infoשלה צריכה להחזירBike: {brand}
main.rs: הכניסו את מודול כלי הרכב והשתמשו בקלטים כדי להדגים שליחה דינמית. צרו משתנהBox<dyn Rentable>שמחזיק תחילהCar, הדפיסו את פרטי ההשכרה שלו, ואז הקצו מחדש לאותו משתנהBikeוהדפיסו שוב את פרטי ההשכרה שלו.
הקסם כאן הוא שמשתנה יחיד מסוג Box<dyn Rentable> יכול להחזיק Car או Bike: Rust קובעת איזו מתודת rental_info לקרוא בזמן ריצה. זו שליחה דינמית בפעולה!
הפלט שלכם צריך להציג את המידע של שני כלי הרכב:
Car: {model}
Bike: {brand}לדוגמה, עם הקלטים Tesla Model 3 ו-Trek:
Car: Tesla Model 3
Bike: Trekתקבלו שני קלטים: דגם המכונית ומותג האופניים.
נסו בעצמכם
mod vehicle;
use vehicle::{Rentable, Car, Bike};
fn main() {
// קרא את הקלט
let mut car_model = String::new();
std::io::stdin().read_line(&mut car_model).expect("Failed to read line");
let car_model = car_model.trim().to_string();
let mut bike_brand = String::new();
std::io::stdin().read_line(&mut bike_brand).expect("Failed to read line");
let bike_brand = bike_brand.trim().to_string();
// TODO: צור משתנה מסוג Box<dyn Rentable> שמכיל Car
// הדפס את פרטי ההשכרה שלו באמצעות המתודה rental_info()
// TODO: הקצה מחדש לאותו משתנה ערך שמכיל Bike
// הדפס שוב את פרטי ההשכרה שלו
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1מתודות והתנהגות
מבוא לבלוקי מימושהפרמטר selfמתודות ניתנות לשינויפונקציות משויכותבלוקי מימוש מרוביםשרשור מתודותסיכום – פעולות מלבן4פרויקט: חיית מחמד וירטואלית
הגדרת חיית המחמדהאכלת חיית המחמדתרגלו בעצמכם: קומפיילר Rust אונליין