Cél és előfeltétel

Egy kétkönyves katalóguson kapcsolod össze az útvonal fogalmait. A könyvnek saját címe, azonosítója és kölcsönzési állapota lesz. A műveletek nézetet vagy módosítható nézetet kapnak a könyvekre. Nem készítünk adatbázist, felhasználói bejelentkezést vagy teljes könyvtári rendszert; a cél néhány kis, világos szerződésű memóriabeli művelet.

Előfeltétel az enumok és állapotok, valamint a szeletek és nézetek megértése. Minden megoldás külön edition 2024 főprogram. A példák saját beágyazott adatokat használnak, és semmit nem írnak külső tárolóba. A három feladatban fokozatosan építjük fel a keresés, kölcsönzés és visszavétel szabályát.

Az azonosító nem a tömb pozíciója

A katalógusban minden könyv külön nem nulla azonosítót kap, és a kezdő adatokban ezek egyediek. A kettes index és a kettes azonosító nem ugyanaz: egy könyv helye megváltozhat anélkül, hogy a könyv azonossága módosulna. A kereső ezért végigvizsgálja a rekordokat, nem közvetlen tömbindexként használja a kapott azonosítót. Két könyvnél ez egyszerű és jól követhető.

A lecke nem készít új rekordokat felvevő műveletet, ezért az azonosítók egyediségét a megadott kezdő adatok biztosítják. Ha bővítenéd a katalógust, a beszúrásnak is őriznie kellene ezt a szabályt. Nem elegendő remélni, hogy a hívó sosem ad duplikált számot. Ez jó példa arra, hogy a modell és a műveletek együtt hordozzák a rendszer ígéreteit.

Keresésnél a hiány szabályos eredmény

A kereső megosztott könyvszeletet kap, és találat esetén kölcsönzött könyvreferenciát ad vissza. A hiányt Option fejezi ki. A visszaadott referencia az eredeti katalógus könyvére mutat, nem új másolatot készít, ezért a forrásnak a használatáig érvényesnek kell maradnia. Az aláírásban szereplő egyetlen bemeneti referencia miatt a kapcsolódó élettartamot itt a fordító következteti.

A hívó a talált könyv címét megvizsgálhatja, majd később új műveletet végezhet a katalóguson, ha a korábbi olvasó nézetet már nem használja. A sorrend ugyanazt a kölcsönzési elvet követi, amelyet egyetlen String esetén tanultál. A gyűjtemény nem szünteti meg ezeket a szabályokat, csak több adatot fog össze.

Állapotváltás a megfelelő könyvön

Kölcsönözni csak szabad könyvet lehet, nem üres olvasónévvel. A név körüli szóközöket levágjuk a tárolás előtt, hogy egy tisztán szóközös név is elutasítható legyen. Nem próbálunk teljes névellenőrző rendszert építeni; itt ez az egy jól kimondott szabály szerepel. A könyv címe és azonosítója a kölcsönzés során nem változhat.

Az enum szabad változatában nincs olvasónév, a kölcsönzöttben van. Egy már foglalt könyv újabb kölcsönzését változtatás nélkül elutasítjuk. A visszavétel csak a kölcsönzött állapotot váltja szabadra. Egy valós rendszernek naplózásra és tartós adatokra is szüksége lenne, de itt a konkrét állapotváltás helyességét tanuljuk meg közvetlen állításokkal.

1. feladat: keresés azonosító alapján

Definiálj könyvtípust azonosítóval és saját címmel. Írj kereső függvényt, amely könyvszeletből találatot vagy hiányt ad. A könyvek azonosítója tíz és húsz legyen, hogy ne lehessen véletlenül pozíciónak nézni. Ellenőrizd a találat címét, egy nem létező számot és az üres katalógust is.

Megoldás
struct Konyv { id: u32, cim: String }
fn keres(konyvek: &[Konyv], id: u32) -> Option<&Konyv> {
    for konyv in konyvek {
        if konyv.id == id { return Some(konyv); }
    }
    None
}
fn main() {
    let konyvek = [
        Konyv { id: 10, cim: String::from("Abigél") },
        Konyv { id: 20, cim: String::from("Tüskevár") },
    ];
    assert_eq!(keres(&konyvek, 20).map(|k| k.cim.as_str()), Some("Tüskevár"));
    assert!(keres(&konyvek, 99).is_none());
    assert!(keres(&[], 10).is_none());
    assert_eq!(konyvek[0].id, 10);
}

A map itt a találat esetén kiolvasott címet alakítja szövegnézetté; hiánynál None marad. Nem gyűjteményt térképezünk át, hanem egy opcionális eredményt.

2. feladat: kölcsönzés adatvesztés nélkül

Készíts szabad és kölcsönzött állapotot. Egy könyvön a kölcsönző metódus utasítsa el az üres nevet és a már kölcsönzött állapotot. A siker után őrizze meg az első olvasó nevét akkor is, ha második ember próbálkozik. Az állapotot teljes értékként hasonlítsd össze, ne csak a visszatérő igaz vagy hamis jelzést vizsgáld.

Megoldás
#[derive(Debug, PartialEq, Eq)]
enum Allapot { Szabad, Kolcsonozve(String) }
struct Konyv { allapot: Allapot }
impl Konyv {
    fn kolcsonoz(&mut self, nev: &str) -> bool {
        let nev = nev.trim();
        if nev.is_empty() || !matches!(&self.allapot, Allapot::Szabad) {
            return false;
        }
        self.allapot = Allapot::Kolcsonozve(nev.to_owned());
        true
    }
}
fn main() {
    let mut konyv = Konyv { allapot: Allapot::Szabad };
    assert!(!konyv.kolcsonoz("   "));
    assert_eq!(konyv.allapot, Allapot::Szabad);
    assert!(konyv.kolcsonoz(" Eszter "));
    assert!(!konyv.kolcsonoz("Bence"));
    assert_eq!(konyv.allapot, Allapot::Kolcsonozve(String::from("Eszter")));
}

A második név nem írja felül az elsőt. Ez a változatlan elutasítás legfontosabb ellenőrzése, nem csupán egy ismételten visszaadott hamis eredmény.

3. feladat: visszavétel a katalógusból

A teljes könyvtípushoz add vissza az azonosítót és címet. Írj katalógusszintű visszavételt: a megfelelő könyvet keresse, és csak kölcsönzött állapotból váltson szabadra. A második visszavétel és az ismeretlen azonosító legyen sikertelen. A végén ellenőrizd, hogy a könyv címe, azonosítója és a másik rekord állapota nem változott.

Megoldás
#[derive(Debug, PartialEq, Eq)]
enum Allapot { Szabad, Kolcsonozve(String) }
struct Konyv { id: u32, cim: String, allapot: Allapot }
fn visszavesz(konyvek: &mut [Konyv], id: u32) -> bool {
    for konyv in konyvek {
        if konyv.id == id {
            if matches!(&konyv.allapot, Allapot::Szabad) { return false; }
            konyv.allapot = Allapot::Szabad;
            return true;
        }
    }
    false
}
fn main() {
    let mut konyvek = [
        Konyv { id: 10, cim: String::from("Abigél"), allapot: Allapot::Kolcsonozve(String::from("Eszter")) },
        Konyv { id: 20, cim: String::from("Tüskevár"), allapot: Allapot::Szabad },
    ];
    assert!(visszavesz(&mut konyvek, 10));
    assert!(!visszavesz(&mut konyvek, 10));
    assert!(!visszavesz(&mut konyvek, 99));
    assert_eq!(konyvek[0].allapot, Allapot::Szabad);
    assert_eq!(konyvek[0].id, 10);
    assert_eq!(konyvek[0].cim, "Abigél");
    assert_eq!(konyvek[1].allapot, Allapot::Szabad);
}

A keresés és módosítás ugyanazon a bejárt könyvön történik. Nincs szükség a teljes rekord klónozására vagy a cím alapján való bizonytalan kiválasztásra.

Elfogadási feltételek

  • A keresés azonosító alapján működik, ismeretlen számnál és üres katalógusnál hiányt ad.
  • Az üres név nem kölcsönözhet, a második olvasó nem írhatja felül a már tárolt nevet.
  • A visszavétel csak egyszer sikeres, és sem az azonosítót, sem a címet, sem a másik könyvet nem módosítja.

Továbblépés

A típusokkal és kölcsönzésekkel felépített szabályokat most már külvilágból érkező bemenetre is alkalmazhatod. Az argumentumok és használati útmutató leckében előbb a program hívási szerződését rögzíted, majd erre építesz kis parancssori eszközt.