1.9 - תכנון מבני נתונים לפי יחסי בעלות - פתרון
פתרון זהות ובעלות נפרדות¶
נגדיר מאגר של slot יחיד כדי להתמקד בחוזה הזהות. במקרה של גלישת דור לא נשנה שום שדה, וכך הכשל אינו משאיר מקום פנוי עם דור שנעשה בו שימוש חוזר.
#[derive(Clone, Copy)]
struct Id { slot: usize, generation: u64 }
struct Store { generation: u64, value: Option<String> }
impl Store {
fn insert(&mut self, value: String) -> Option<Id> {
if self.value.is_some() { return None; }
self.value = Some(value);
Some(Id { slot: 0, generation: self.generation })
}
fn get(&self, id: Id) -> Option<&str> {
if id.slot != 0 || id.generation != self.generation { return None; }
self.value.as_deref()
}
fn remove(&mut self, id: Id) -> Result<String, &'static str> {
if self.get(id).is_none() { return Err("stale id"); }
let next = self.generation.checked_add(1).ok_or("generation exhausted")?;
let value = self.value.take().ok_or("empty")?;
self.generation = next;
Ok(value)
}
}
fn main() {
let mut store = Store { generation: 0, value: None };
let old = store.insert("first".into()).unwrap();
assert_eq!(store.remove(old).unwrap(), "first");
let new = store.insert("second".into()).unwrap();
assert_eq!(store.get(old), None);
assert_eq!(store.get(new), Some("second"));
assert!(store.remove(old).is_err());
assert_eq!(store.get(Id { slot: 9, generation: new.generation }), None);
}
המאגר מחזיק את String, ואילו Id מחזיק רק מידע לבדיקת זהות. ההפניה שמתקבלת מ־get קשורה להשאלה של המאגר, ולכן אינה יכולה לשרוד שינוי שסותר את השאלתה. במסלול exhausted הערך נשאר במאגר, והקורא יכול להחליט כיצד לטפל במגבלת הזהויות.