Cél és előfeltétel

Képzelj el egy kis raktári készletet: minden terméknek neve és darabszáma van. Külön változókkal könnyű eltéveszteni, melyik szám melyik névhez tartozik. A struct az összetartozó adatokat egyetlen értékbe rendezi, és jelentést ad a mezők neveinek. Ebben a leckében ezt a szerkezetet a rajta végzett műveletekkel is összekötöd.

Előfeltétel a kölcsönzés és a szeletek megértése. A példák külön edition 2024 programok, nem valós raktárnyilvántartás részei. Mindegyiknél írd le, milyen állapotnak kell megmaradnia sikertelen művelet után is. A változatlan adat éppúgy ellenőrizendő eredmény, mint a sikeres fogyás.

A mezőnév a jelentés része

A struct definíciója megadja a mezők nevét és típusát. Példány létrehozásakor minden szükséges mezőnek értéket kell adnod. A terméknév saját String, a darabszám nem negatív egész. A mezőnevek miatt nem kell emlékezned arra, hogy egy névtelen zárójeles pár melyik pozíciója hordozta a mennyiséget. A fordító a mező elírását is észreveheti.

A típus még nem garantál minden üzleti szabályt. Egy String lehet üres, egy darabszám pedig lehet a vártnál nagyobb. A jelen lecke a készlet kivételének szabályát kezeli: csak akkor csökkentünk, ha van elég darab. Nem állítjuk, hogy a puszta struct minden lehetséges hibás termékadat létrehozását megakadályozza. A típus szerkezetet ad, a művelet további feltételeket őriz.

A metódushoz tartozó hozzáférés

Az impl blokkban a típushoz tartozó függvényeket írhatod le. A metódus első paramétere self valamelyik formája. A megosztott referencia csak olvasást kér, ezért egy megjelenítő vagy készletvizsgáló műveletnek általában ez elegendő. A módosítható referencia változtatást enged anélkül, hogy a teljes termék tulajdonát elvinné a hívótól.

A ponttal hívott metódus nem különleges jogú varázslat: ugyanazokat a tulajdoni és kölcsönzési szabályokat követi, mint a korábbi függvényeink. Az aláírásból továbbra is kiolvasható, hogy csak megnézi vagy átírja az értéket. Ha a metódus önmagát értékként venné át, az a tulajdon elfogyasztását is jelenthetné; erre most nincs szükségünk a készlet kezelésénél.

Kapcsolódó függvény és ellenőrzött változtatás

A típushoz tartozhat olyan függvény is, amely nem kap self paramétert. A példában ez készít egy induló terméket. A kettős kettősponttal hívott létrehozó függvény neve lehet uj, de ez választott elnevezés, nem a nyelv kötelező konstruktorszintaxisa. A függvény visszaadott értéke ugyanúgy a hívó tulajdonába kerül, mint korábban.

Módosításnál előbb ellenőrizd a feltételt, és csak siker esetén írj adatot. Ha előbb csökkentenéd a darabszámot, majd később vizsgálnád az elegendő készletet, helyreállítással és esetleges túlcsordulással nehezítenéd a programot. A sikertelen kérés maradjon megfigyelhető hamis eredmény, a korábbi készlet pedig változatlan. Ehhez nem elég csak a logikai visszatérést tesztelni.

1. feladat: olvasható termékadat

Definiálj egy Termek típust saját névvel és egész darabszámmal. Készíts két eltérő példányt, majd ellenőrizd, hogy az egyik neve és a másik készlete valóban a megfelelő mezőből érkezik. Egyik példányt se módosítsd. A két eltérő adat segít észrevenni, ha tévesen ugyanazt a példányt olvasnád mindkét állításban.

Megoldás
struct Termek { nev: String, darab: u32 }
fn main() {
    let ceruza = Termek { nev: String::from("ceruza"), darab: 12 };
    let radir = Termek { nev: String::from("radír"), darab: 0 };
    assert_eq!(ceruza.nev, "ceruza");
    assert_eq!(ceruza.darab, 12);
    assert_eq!(radir.nev, "radír");
    assert_eq!(radir.darab, 0);
}

A nulla készlet nem hiányzó mező, hanem szabályos egész érték. A név és a darabszám ugyanahhoz a termékhez tartozik, mégis külön típusú adatként olvasható.

2. feladat: létrehozás és olvasó metódus

Írj a termékhez uj kapcsolódó függvényt, amely a nevet szövegnézetként kapja, és saját String értéket tárol. Az elerheto metódus jelezze, hogy a készlet nagyobb-e nullánál. Ellenőrizd a pozitív és nulla készletet, majd a nevet is használd a metódushívás után. Így látható, hogy az olvasás nem vitte el a tulajdont.

Megoldás
struct Termek { nev: String, darab: u32 }
impl Termek {
    fn uj(nev: &str, darab: u32) -> Self {
        Self { nev: nev.to_owned(), darab }
    }
    fn elerheto(&self) -> bool { self.darab > 0 }
}
fn main() {
    let ceruza = Termek::uj("ceruza", 12);
    let radir = Termek::uj("radír", 0);
    assert!(ceruza.elerheto());
    assert!(!radir.elerheto());
    assert_eq!(ceruza.nev, "ceruza");
    assert_eq!(radir.nev, "radír");
}

A to_owned itt szándékosan saját szöveget készít: a terméknek a bemeneti nézet élettartamától függetlenül kell megőriznie a nevét. Ez más cél, mint az ideiglenes olvasási kölcsönzés.

3. feladat: kivétel állapotrombolás nélkül

Írj csökkentő metódust egy egyszerű Keszlet típushoz. Ha a kérés nagyobb a meglévő darabszámnál, térjen vissza hamissal, és ne változtasson semmit. A sikeres csökkentés igazat adjon. A nulla darabos kivételt elfogadjuk változás nélkül. Ugyanazon a példányon próbáld ki a sikert és az elutasítást.

Megoldás
struct Keszlet { darab: u32 }
impl Keszlet {
    fn kivesz(&mut self, kert: u32) -> bool {
        if kert > self.darab { return false; }
        self.darab -= kert;
        true
    }
}
fn main() {
    let mut keszlet = Keszlet { darab: 5 };
    assert!(keszlet.kivesz(2));
    assert_eq!(keszlet.darab, 3);
    assert!(!keszlet.kivesz(4));
    assert_eq!(keszlet.darab, 3);
    assert!(keszlet.kivesz(0));
    assert!(keszlet.kivesz(3));
    assert_eq!(keszlet.darab, 0);
}

Az elutasított négyes kérés után a hármas készlet megmarad. A végső pontos kivétel azt is igazolja, hogy az egyenlő határ elfogadott, nem csak a szigorúan kisebb kérés.

Elfogadási feltételek

  • A két termék neve és darabszáma mezőnév alapján, egymástól függetlenül ellenőrzött.
  • Az olvasó metódus nulla készletnél hamis, pozitívnál igaz, a tulajdonos utána is használható.
  • A kivétel elutasításkor változatlan állapotot hagy, pontos készletnél és nulla kérésnél sikeres.

Továbblépés

A struct mezői összetartozó adatokat rendeznek. Az enumok és állapotok leckében azt is kifejezed, amikor egy érték több egymást kizáró eset valamelyikében lehet, és az esethez külön adat tartozhat.