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.