שיעורי מודול (2/2)
מצביעים חכמים: Box ו-Rc
מצביע חכם הוא מבנה נתונים שמתנהג כמו מצביע, אך יש לו מטא נתונים ויכולות נוספות (כגון ספירת הפניות או ניהול זיכרון אוטומטי).
ב-Rust, הפניות רגילות (כגון &) שואלות רק נתונים. מצביעים חכמים, לעומת זאת, מחזיקים לעתים קרובות בנתונים שהם מצביעים עליהם. דוגמאות נפוצות כוללות את String ו-Vec<T>, אך הספרייה הסטנדרטית מספקת ספרים מיוחדים נוספים.
תיבה<T> להקצאת ערימה
המצביע החכם הפשוט ביותר הוא Box<T>, המאחסן נתונים על ערימה ולא בערימה. רק המצביע לנתונים שהוקצו בערימה נשאר בערימה.
הוא משמש בעיקר במקרים הבאים:
- כאשר יש לך סוג שלא ניתן לדעת את גודלו בזמן ההידור (כמו טיפוס רקורסיבי), ואתה רוצה להשתמש בערך מהסוג הזה בהקשר שדורש גודל מדויק.
- כאשר רוצים להעביר בעלות על כמות גדולה של נתונים מבלי להעתיק אותם.
דוגמה בסיסית:
fn main() {
let b = Box::new(5); // allocates the value 5 on the heap
println!("b = {}", *b); // dereferences with * to read the value
}רשימות סוגים וחסרונות רקורסיביים
טיפוס רקורסיבי הוא טיפוס שיכול להכיל ערך של עצמו כחלק מהגדרתו. חלודה חייבת לדעת את הגודל המדויק של סוג בזמן ההידור. מכיוון שהעומק של טיפוס רקורסיבי יכול להיות אינסופי, המהדר יוצר שגיאה אלא אם כן אתה משתמש ב-Box (שיש לו גודל קבוע, מכיוון שהוא מצביע):
enum List {
Cons(i32, Box<List>),
Nil,
}Rc<T>: ספירת הפניות
ישנם מקרים שבהם לערך בודד יכולים להיות מספר בעלים. המצביע החכם Rc<T> (ספירת הפניות) עוקב אחר מספר ההפניות לערך כדי לקבוע אם הערך עדיין בשימוש. אם הספירה יורדת לאפס, המשאב מוקצה בבטחה.
[!הערה]
Rc<T>מתאים רק לתרחישים עם חוטים בודדים. עבור תרחישים מרובי-הליכים (במקביל), עליך להשתמש ב-Arc<T>(נספרת התייחסות אטומית).
use std::rc::Rc;
fn main() {
let a = Rc::new(5);
let b = Rc::clone(&a); // increments the reference count to 2
println!("References: {}", Rc::strong_count(&a));
}נסה זאת בעצמך
תרגיל 1: הקצאת ערמות עם תיבה
הקצו ערך שלם של 42 בערימה באמצעות Box::new והקצו אותו למשתנה בשם val. לאחר מכן, הדפס את הערך המאוחסן ב-Box על ידי הפניה מפורשת שלו עם האופרטור *.
הצג רמז
השתמש ב-'let val = Box::new(42);' כדי לאחסן את הערך בערימה ולהגדיר אותו עם '*val' בתוך 'println!'.
הפתרון זמין לאחר 3 ניסיונות
תרגיל 2: מבנה נתונים רקורסיבי (רשימת חסרונות)
הגדר רשימה רקורסיבית בשם רשימה המכיל שתי גרסאות: Cons אשר עוטף tuple (i32, Box<List>), ו-Nil המייצג את סוף הרשימה. בפונקציה הראשית, הצג רשימה מקושרת המכילה את האלמנט 1 ואחריו Nil.
הצג רמז
ה-enum מוכרז בתור `enum List { Cons(i32, Box<List>), Nil }`. הצג את הרשימה באמצעות `List::Cons(1, Box::new(List::Nil))`.
הפתרון זמין לאחר 3 ניסיונות
תרגיל 3: שיתוף בעלות עם Rc
השתמש ב-std::rc::Rc כדי ליצור מספר שלם משותף המכיל את הערך 100 במשתנה a. שכבו את ההפניה למשתנה b באמצעות Rc::clone. לבסוף, הדפס את ספירת הפניות הפעילות באמצעות הפונקציה Rc::strong_count.
הצג רמז
צור את הערך עם `Rc::new(100)`. שכפל עם `Rc::clone(&a)`והצג את ספירת הבעלים עם`Rc::strong_count(&a)`.
הפתרון זמין לאחר 3 ניסיונות