Cél és előfeltétel
Ebben a leckében szöveges bemenetből készítesz ellenőrzött adatot. A cél, hogy külön kezeld az elfogadható hiányt, a hibás formátumot és az üzleti szabály megsértését. A feladatok egy egyszerű jegyfoglalás köré épülnek, de ugyanazt a gondolkodást használhatod konfiguráció, parancssori argumentum vagy űrlap feldolgozásakor is.
Előfeltétel A Rust alapjai, valamint az előző structos és enumos adatmodellezési lecke. Már tudj kölcsönözni és match kifejezést írni. Minden Rust-blokk önálló program: külön futtasd őket egy edition 2024 projektben a cargo run paranccsal. Az állítások sikeres bemenetet és elutasított eseteket egyaránt ellenőriznek.
A hiány és a hiba más kérdésre válaszol
Az Option<T> azt fejezi ki, hogy van-e érték. Egy választható becenév hiánya természetes lehet, ezért önmagában nem hiba. A Result<T, E> viszont sikeres eredményt vagy a sikertelenség okát adja vissza. Ha a felhasználó betűket írt a jegyszám helyére, nem egyszerűen hiányzik egy szám: a megadott értéket nem tudtuk elfogadni.
Ne jelöld a hiányt kitalált adatokkal. A nulla lehet valódi mérés, az üres szöveg lehet hibás bevitel, ezért nem mindig alkalmasak helykitöltőnek. Először döntsd el a szerződést: kötelező-e a mező, és mi számít érvényes tartalomnak? Csak utána válassz visszatérési típust. Azonos technikai bemenet két alkalmazásban eltérő döntéshez vezethet.
fn megszolitas(becenev: Option<&str>) -> &str {
match becenev {
Some(nev) if !nev.trim().is_empty() => nev.trim(),
_ => "Vendég",
}
}
fn main() {
assert_eq!(megszolitas(None), "Vendég");
assert_eq!(megszolitas(Some(" ")), "Vendég");
assert_eq!(megszolitas(Some(" Éva ")), "Éva");
}
Itt kifejezetten megengedtük a hiányt és az üres becenevet, ezért a közös alapérték nem fed el elutasítandó állapotot. A függvény kölcsönzött szöveget ad vissza, nem készít felesleges másolatot. Ez nem általános szabály minden névmezőre: a foglalás kötelező nevénél már hibát fogunk jelezni.
Ellenőrzési sorrend és beszédes hibák
A beolvasás és az érvényességvizsgálat külön lépés. Egy szöveg lehet szabályos u16 szám, de ettől még nem fér bele az alkalmazásban engedélyezett egy és nyolc közötti tartományba. Előbb távolítsd el a szélső whitespace karaktereket, utána értelmezd a számot, végül ellenőrizd a megengedett értékeket. A belső szóközöket a trim nem tünteti el.
A ? az aktuális függvényből tér vissza sikertelenségkor; nem állítja le automatikusan az egész programot. A map_err a technikai hibát a saját hibaváltozatodra alakíthatja. Ez tudatos egyszerűsítés: a példában a felületnek azt fontos tudnia, melyik mezőt kell javítani, nem a számfeldolgozó belső hibaüzenetét. Az unwrap ezzel szemben pánikot okozna elutasított bemenetnél, ezért erre a feladatra nem megfelelő.
A példák az első hibánál visszatérnek. Ez egyszerű feldolgozási szerződés, nem minden felület kötelező működése. Egy összetett űrlapnál hasznos lehet valamennyi hibát egyszerre megmutatni, de ahhoz külön gyűjtenünk kellene az eredményeket. Itt azt gyakoroljuk, hogyan tudja a hívó egyetlen döntési ponton megkülönböztetni a sikert és az elutasítást. A hibaváltozat neve a programnak szól; a felhasználói mondatot később egy külön megjelenítő réteg fogalmazhatja meg. Így egy szövegezési javítás miatt nem kell átírni a számellenőrzést, és a tesztek sem emberi hibaüzenetek betű szerinti alakjától függenek. Az elutasítandó bemenet javítható felhasználói helyzet, nem feltétlenül programozási hiba.
1. feladat – Választható kedvezménykód
Írj függvényt, amely választható kedvezménykódot kap. A kód hiánya nulla százalékot jelent, a pontosan DIAK kód tíz százalékot. A megadott, de csak szóközökből álló kód legyen külön hiba; minden más kód ismeretlen. A kisbetűs alakot most nem fogadjuk el. A feladat célja a hiány és a hibásan megadott érték szétválasztása.
Megoldás
#[derive(Debug, PartialEq, Eq)]
enum KodHiba {
Ures,
Ismeretlen,
}
fn kedvezmeny(kod: Option<&str>) -> Result<u8, KodHiba> {
match kod.map(str::trim) {
None => Ok(0),
Some("") => Err(KodHiba::Ures),
Some("DIAK") => Ok(10),
Some(_) => Err(KodHiba::Ismeretlen),
}
}
fn main() {
assert_eq!(kedvezmeny(None), Ok(0));
assert_eq!(kedvezmeny(Some(" ")), Err(KodHiba::Ures));
assert_eq!(kedvezmeny(Some(" DIAK ")), Ok(10));
assert_eq!(kedvezmeny(Some("diak")), Err(KodHiba::Ismeretlen));
}
Az Option::map csak jelen lévő értékre futtatja a megadott függvényt, a hiányt megőrzi. Nem alakítjuk át az ismeretlen kódot csendben nulla kedvezménnyé: az eltüntetné a felhasználó javítható elírását. A kódlista bővítésekor a hozzá tartozó teszteket is egészítsd ki.
2. feladat – Számformátum és megengedett tartomány
Készíts darab függvényt, amely szövegből egy és nyolc közötti, előjel nélküli egész jegyszámot olvas. Az üres mezőt, a u16 típusként nem olvasható szöveget és a szabályos, de megengedett tartományon kívüli számot különböztesd meg. Ellenőrizd mindkét határértéket, a nullát, a kilencet és egy túl nagy számot is.
Megoldás
#[derive(Debug, PartialEq, Eq)]
enum DarabHiba {
Ures,
NemOlvashato,
Ertekhatar,
}
fn darab(szoveg: &str) -> Result<u16, DarabHiba> {
let szoveg = szoveg.trim();
if szoveg.is_empty() {
return Err(DarabHiba::Ures);
}
let ertek = szoveg.parse::<u16>().map_err(|_| DarabHiba::NemOlvashato)?;
if !(1..=8).contains(&ertek) {
return Err(DarabHiba::Ertekhatar);
}
Ok(ertek)
}
fn main() {
assert_eq!(darab("1"), Ok(1));
assert_eq!(darab(" 8 "), Ok(8));
assert_eq!(darab("0"), Err(DarabHiba::Ertekhatar));
assert_eq!(darab("9"), Err(DarabHiba::Ertekhatar));
assert_eq!(darab(""), Err(DarabHiba::Ures));
assert_eq!(darab("kettő"), Err(DarabHiba::NemOlvashato));
assert_eq!(darab("-1"), Err(DarabHiba::NemOlvashato));
assert_eq!(darab("65536"), Err(DarabHiba::NemOlvashato));
}
A negatív szám és a 65536 is sikertelen u16 átalakítás. Ettől eltér a kilenc: az technikailag ábrázolható, csak ebben a foglalásban túl sok. Ezzel a különbséggel értelmes hibajelzést készíthetsz anélkül, hogy minden rossz bevitelre ugyanazt az üzenetet adnád.
3. feladat – Ellenőrzött foglalás több lépésben
Készíts Foglalas structot névvel és jegyszámmal. A létrehozó függvény először a kötelező nevet, majd a jegyszámot ellenőrizze. Őrizze meg a hiba okát, és csak teljes siker után készítse el a rekordot. Ha mindkét mező hibás, a szerződés szerint a név hibáját kapjuk először. Így a feldolgozás sorrendje tesztelhető és kiszámítható.
Megoldás
#[derive(Debug, PartialEq, Eq)]
enum Hiba {
UresNev,
UresDarab,
NemOlvashatoDarab,
Ertekhatar,
}
#[derive(Debug, PartialEq, Eq)]
struct Foglalas {
nev: String,
darab: u16,
}
fn darab(szoveg: &str) -> Result<u16, Hiba> {
let szoveg = szoveg.trim();
if szoveg.is_empty() {
return Err(Hiba::UresDarab);
}
let ertek = szoveg.parse::<u16>().map_err(|_| Hiba::NemOlvashatoDarab)?;
if !(1..=8).contains(&ertek) {
return Err(Hiba::Ertekhatar);
}
Ok(ertek)
}
fn foglalas(nev: &str, darab_szoveg: &str) -> Result<Foglalas, Hiba> {
let nev = nev.trim();
if nev.is_empty() {
return Err(Hiba::UresNev);
}
let darab = darab(darab_szoveg)?;
Ok(Foglalas { nev: nev.to_owned(), darab })
}
fn main() {
assert_eq!(foglalas(" Éva ", "2"), Ok(Foglalas {
nev: String::from("Éva"), darab: 2,
}));
assert_eq!(foglalas(" ", "hibás"), Err(Hiba::UresNev));
assert_eq!(foglalas("Éva", ""), Err(Hiba::UresDarab));
assert_eq!(foglalas("Éva", "sok"), Err(Hiba::NemOlvashatoDarab));
assert_eq!(foglalas("Éva", "9"), Err(Hiba::Ertekhatar));
}
A két függvény azonos hibatípust használ, ezért a ? közvetlenül továbbadhatja az elutasítás okát. A rekord még nem létezik, amikor a darabszám ellenőrzése elbukik. Nincs félkész foglalás, amelyet utólag kellene eltakarítani. A második feladat önálló példája és ez a megoldás külön fájlba való, nem egymás mögé másolandó.
Elfogadási feltételek
- A hiányzó kedvezménykód sikeres nulla kedvezmény, de az üres vagy ismeretlen, megadott kód hiba.
- A jegyszám egy és nyolc között, a két határt is beleértve elfogadható; a hibás formátum és az értékhatár külön eset.
- Hibás foglalásnál beszédes
Errkeletkezik pánik helyett, és a tesztek az ellenőrzések sorrendjét is igazolják.
Összefoglaló és továbblépés
Az ellenőrzés attól lesz használható, hogy megfogalmazod a siker és a különböző elutasítások jelentését. Az Option a hiányt, a Result a sikert vagy hibát teszi láthatóvá; a ? pedig egyszerűsíti a következetes továbbadást. A következő Vec és slice gyakorlatok leckében több értéket dolgozunk fel, és az üres gyűjteményt is ilyen tudatosan kezeljük.