8.2 - קיבוע כתובת והבטחת ההשמדה - פתרון
פתרון תרגול ההצמדה¶
פתרון ראשון - הבעלות מטיילת¶
הוסיפו לתוכנית מההרצאה את הפונקציות הבאות, והחליפו את גוף הפונקציה הראשית בגוף המוצג. הקוד משתמש באותו מימוש של AddressChecked.
fn pass(value: Pin<Box<AddressChecked>>) -> Pin<Box<AddressChecked>> {
value
}
fn through_vector(value: Pin<Box<AddressChecked>>) -> Pin<Box<AddressChecked>> {
let mut values = vec![pass(value)];
pass(values.pop().unwrap())
}
fn main() {
let original = AddressChecked::new(17);
let address = original.as_ref().get_ref() as *const AddressChecked;
let returned = through_vector(original);
assert_eq!(address, returned.as_ref().get_ref() as *const AddressChecked);
assert_eq!(returned.as_ref().check(), 17);
drop(returned);
}
המעטפת עוברת בין פרמטרים, איבר בווקטור וערך מוחזר. ייתכן שאחסון המעטפת עצמה משתנה בכל מעבר, אבל היא ממשיכה להצביע לאותה הקצאה. הוצאת המעטפת מהווקטור אינה מוציאה את האובייקט מהקופסה. הפלט הצפוי הוא drop: 17 פעם אחת.
פתרון שני - השמדה לפני התקנה¶
הדוגמה הבאה היא תוכנית עצמאית. היא אינה שומרת מצביע לעצמה ולכן אינה צריכה לאתחל כתובת לאחר ההחלפה.
use std::marker::PhantomPinned;
struct Item {
number: u32,
_pinned: PhantomPinned,
}
impl Item {
fn new(number: u32) -> Self {
Self { number, _pinned: PhantomPinned }
}
}
impl Drop for Item {
fn drop(&mut self) {
println!("drop: {}", self.number);
}
}
fn main() {
let mut item = Box::pin(Item::new(1));
item.as_mut().set(Item::new(2));
println!("replacement installed");
drop(item);
}
הפלט הצפוי מראה שהערך הישן מושמד לפני ההודעה על ההתקנה, והערך החדש מושמד בסוף.
הערך הישן אינו מוחזר למשתמש ואינו ממשיך לחיות במקום אחר. לכן ההחלפה אינה מפרה את דרישת הכתובת היציבה במהלך חייו. אסור להחליף את הפעולה הזאת ב־mem::replace דרך גישה שהושגה באופן לא בטוח, משום שפעולה כזאת מחלצת ערך חי מהמקום שאליו הוצמד.
פתרון שלישי - חוזים סותרים¶
הממשק הראשון מבטיח שהשדה מוצמד. הממשק השני מאפשר למקבל לבצע עליו mem::replace, ולכן הוא מאפשר להזיז את אותו שדה בלי להשמיד אותו במקומו. שתי ההבטחות אינן מתיישבות עבור טיפוס שאינו מקיים Unpin.
התיקון הראשון הוא לדרוש F: Unpin במתודה שמחזירה השאלה רגילה. כך המתודה זמינה רק כאשר הסרת מגבלת ההצמדה אינה פוגעת בטיפוס. התיקון השני הוא להסיר את הגישה הרגילה ולהציע פעולות מוגבלות דרך ההקרנה המוצמדת. במקרה הזה המשתמש מאבד את היכולת להחליף או לחלץ את השדה באופן שרירותי, אבל הממשק ממשיך לתמוך בעתידים תלויי כתובת.
ההוכחה צריכה לכלול גם את ההורס של המבנה החיצוני ואת כל פעולות האתחול שלו. תיקון חתימה אחת אינו מספיק אם מתודה אחרת עדיין מזיזה את השדה מאחורי הקלעים.