לדלג לתוכן

2.9 - אתחול מאוחר וחוזי כשל בממשק - פתרון

פתרון אימות לפני commit

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

#[derive(Debug, PartialEq)]
struct Config { workers: u32, queue: u32 }

impl Config {
    fn reload(&mut self, text: &str) -> Result<(), &'static str> {
        let (workers, queue) = text.split_once(',').ok_or("missing separator")?;
        let workers: u32 = workers.parse().map_err(|_| "invalid workers")?;
        let queue: u32 = queue.parse().map_err(|_| "invalid queue")?;
        if workers == 0 || queue == 0 { return Err("zero value"); }
        if queue < workers { return Err("queue too small"); }
        *self = Config { workers, queue };
        Ok(())
    }
}

fn main() {
    let mut config = Config { workers: 2, queue: 8 };
    for text in ["bad", "0,8", "8,2"] {
        assert!(config.reload(text).is_err());
        assert_eq!(config, Config { workers: 2, queue: 8 });
    }
    config.reload("4,16").unwrap();
    assert_eq!(config, Config { workers: 4, queue: 16 });

    let cell = std::cell::OnceCell::<Result<u32, String>>::new();
    assert!(cell.get_or_init(|| Err("failed".into())).is_err());
    assert!(cell.get_or_init(|| Ok(42)).is_err());
}

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