Az előző leckében megismerted a skalár és összetett típusokat — most itt az ideje, hogy ezekkel valamit is csináljunk. Egy program ritkán fut le egyenes vonalban felülről lefelé: döntéseket kell hoznia ("ha ez igaz, csináld ezt, ha nem, csináld azt"), és gyakran ugyanazt a műveletet többször meg kell ismételnie. Ebben a leckében a Rust három alapvető vezérlési eszközét nézzük végig: a feltételes elágazást (if/else), a mintaillesztést (match), és a ciklusokat (loop, while, for).
Feltételes elágazás: if és else
A Rust if-je nagyon hasonlít más nyelvekéhez, egy fontos különbséggel: a feltétel köré nem kell zárójel, a kapcsos zárójelek pedig kötelezőek — nincs egysoros "zárójel nélküli" if.
fn main() {
let temperature = 21;
if temperature > 25 {
println!("Meleg van, {temperature} fok.");
} else if temperature > 15 {
println!("Kellemes idő, {temperature} fok.");
} else {
println!("Hűvös van, {temperature} fok.");
}
}
Fontos, hogy a feltételnek mindig bool típusúnak kell lennie — a Rust nem alakít át automatikusan más típust (pl. 0-t vagy ""-t) igazságértékké, mint néhány dinamikus nyelv.
fn main() {
let count = 0;
if count {
println!("ez nem fog fordulni");
}
}
error[E0308]: mismatched types
--> src/main.rs:4:8
|
4 | if count {
| ^^^^^ expected `bool`, found integer
Az if-nek van egy különleges tulajdonsága is: kifejezésként is használható, azaz visszaadhat egy értéket, amit el is tárolhatsz egy let-tel. Ilyenkor minden ágnak ugyanolyan típust kell visszaadnia, és nincs pontosvessző az utolsó kifejezés után:
fn main() {
let temperature = 8;
let advice = if temperature < 10 {
"Vegyél fel kabátot!"
} else {
"Póló is elég lesz."
};
println!("{advice}");
}
Ez az if-kifejezés minta gyakran szebb, mint egy let mut deklaráció, amit utána feltételesen módosítasz — érdemes megszokni.
Mintaillesztés: a match alapjai
A match a Rust egyik legkedveltebb szerkezete: egy értéket több mintával hasonlít össze, és lefuttatja az elsőt, amelyik illik. Egy nagy előnye, hogy kimerítőnek (exhaustive) kell lennie — a fordító megköveteli, hogy minden lehetséges esetet lefedj, különben nem fordul a kód. Ez sok futásidejű hibától megóv.
fn main() {
let day_number = 3;
let day_name = match day_number {
1 => "hétfő",
2 => "kedd",
3 => "szerda",
4 => "csütörtök",
5 => "péntek",
6 | 7 => "hétvége",
_ => "ismeretlen nap",
};
println!("A {day_number}. nap: {day_name}");
}
A | jellel egy ágban több mintát is megadhatsz ("vagy"), az alávonás (_) pedig a "minden más eset" jokerkarakter — ha kihagynád, a fordító hibát dobna, mert nem minden i32 értékre lenne definiálva a viselkedés.
A match ágakban tartományokat (range-eket) is használhatsz, ami különösen kényelmes számokra vagy karakterekre:
fn main() {
let score = 78;
let grade = match score {
90..=100 => 'A',
80..=89 => 'B',
70..=79 => 'C',
60..=69 => 'D',
_ => 'F',
};
println!("A {score} pontszámhoz tartozó jegy: {grade}");
}
A 90..=100 egy zárt tartomány (inclusive range): mindkét végpontját beleérti. Ha csak 90..100-at írnál, akkor a 100 már nem tartozna bele.
A match sokkal többre képes, mint amit itt látsz — tuple-öket, több változót egyszerre és bonyolultabb mintákat is tud illeszteni. Most csak az alapokra koncentrálunk, a mélyebb lehetőségek egy későbbi leckében jönnek elő.
Ismétlés: loop, while és for
A Rustban három ciklusfajta van, mindegyiknek megvan a maga jó helye.
A loop a legegyszerűbb: a végtelenségig fut, amíg egy break-kel ki nem lépsz belőle. Előnye, hogy a break után adhatsz vissza egy értéket is:
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 5 {
break counter * 2;
}
};
println!("Az eredmény: {result}");
}
A while ciklus addig fut, amíg egy feltétel igaz:
fn main() {
let mut fuel = 5;
while fuel > 0 {
println!("Üzemanyag: {fuel}");
fuel -= 1;
}
println!("Kifogyott az üzemanyag!");
}
A for ciklus pedig egy sorozaton lépked végig — ez a leggyakrabban használt forma, mert nem kell manuálisan kezelned a számlálót:
fn main() {
let numbers = [10, 20, 30, 40];
for number in numbers {
println!("Szám: {number}");
}
}
A for ciklus a háttérben azt a mechanizmust használja, amivel a Rust bármin végig tud iterálni — erről (az Iterator trait-ről) egy későbbi leckében tanulsz majd részletesen. Most elég annyit tudnod, hogy tömbökön és range-eken simán működik.
Range-ek: 0..10 és 0..=10
A for ciklust nagyon gyakran range-ekkel kombináljuk, ha egyszerűen csak számolni akarunk. A 0..10 egy félig zárt tartomány: 0-tól 9-ig tart (a 10 nincs bele), míg a 0..=10 zárt tartomány, 0-tól 10-ig.
fn main() {
println!("Félig zárt tartomány:");
for i in 0..5 {
println!(" i = {i}");
}
println!("Zárt tartomány:");
for i in 0..=5 {
println!(" i = {i}");
}
}
Ha csökkenő sorrendben szeretnél végigmenni számokon, azt a for ciklus önmagában (range-dzsel) nem tudja megtenni fordított irányban — erre majd egy későbbi leckében kapsz eleganciás eszközt. Most egyszerűen while ciklussal is megoldható:
fn main() {
let mut countdown = 5;
while countdown > 0 {
println!("{countdown}...");
countdown -= 1;
}
println!("Indulás!");
}
Gyakorlati példa: mindent egybe
Nézzünk egy programot, amely az if, a match és a for ciklust is kombinálja: 1-től 20-ig kiírja a számokat, és minden szám mellé odaírja, hogy hányassal osztható.
fn main() {
for number in 1..=20 {
let divisible_by_3 = number % 3 == 0;
let divisible_by_5 = number % 5 == 0;
let label = match (divisible_by_3, divisible_by_5) {
(true, true) => "Fizzbuzz",
(true, false) => "Fizz",
(false, true) => "Buzz",
(false, false) => "",
};
if label.is_empty() {
println!("{number}");
} else {
println!("{number} -> {label}");
}
}
}
Itt egy tuple-t ((divisible_by_3, divisible_by_5)) illesztünk a match-csel — ez jól mutatja, hogy a match több változó kombinációját is egyszerűen kezelheti, nem csak egyetlen értéket.
Próbáld ki!
Írj egy programot, amely egy let age = 17; sorral indul, és if/else if/else szerkezettel kiírja, hogy a személy "gyerek" (0-12), "tinédzser" (13-17) vagy "felnőtt" (18 fölött). Ezután írd meg ugyanezt match-csel is, ..= tartományokkal — melyik verziót találod olvashatóbbnak?
Önellenőrzés
1. Miért kell a match kifejezésnek kimerítőnek lennie, és mi történik, ha kihagysz egy esetet?
Megoldás
A Rust fordító megköveteli, hogy a match minden lehetséges bemeneti értékre definiáljon egy viselkedést, mert így garantálható, hogy futásidőben soha nem marad kezeletlen eset. Ha kihagysz egy esetet (és nincs _ joker ág), fordítási hibát kapsz — ez sok potenciális hibát kiszűr még azelőtt, hogy a program elindulna.
2. Mi a különbség a 0..5 és a 0..=5 range között?
Megoldás
A 0..5 félig zárt (exclusive) tartomány: 0, 1, 2, 3, 4 — a felső határ (5) nincs bele. A 0..=5 zárt (inclusive) tartomány: 0, 1, 2, 3, 4, 5 — a felső határ is beletartozik.
3. Mikor válasszunk loop-ot while helyett?
Megoldás
A loop-ot érdemes választani, ha nincs egyszerű, előre megfogalmazható feltétel a ciklus elején (pl. a kilépési feltétel csak a ciklus közepén derül ki), vagy ha a ciklusból egy értéket is vissza akarsz adni a break segítségével. A while-t akkor használd, ha van egy tiszta, a ciklus elején kiértékelhető logikai feltételed.
Összefoglalás
- Az
if/elsea Rustban is feltételes elágazásra szolgál, de kifejezésként is használható, így egyenesen egylet-be írhatod az eredményét. - A
matchkimerítő mintaillesztést biztosít: minden esetet le kell fednie, ami sok futásidejű hibát megelőz — használhatsz benne konkrét értékeket,|-lel több minta,_jokert és..=tartományokat is. - A
loop,whileésformind ismétlésre szolgál, de más a jó felhasználási területük:loopvégtelen/feltétel nélküli ismétlésre és értékvisszaadásra,whilefeltétel-vezérelt ismétlésre,forpedig sorozatokon (tömbök, range-ek) való egyszerű végigjárásra. - A
0..10és0..=10range-ek gyakori társai aforciklusnak, és amatchágakban is jól használhatók. - A következő leckében egy ellenőrző kvíz vár rád, amiben összefoglalod, mennyire ülnek már az eddig tanult alapok — az if/else, match és ciklusok mind visszatérnek benne.
Ha mélyebben el akarsz merülni a témában, nézd meg a Rust hivatalos könyvének vezérlési szerkezetekről szóló fejezetét: Control Flow – The Rust Programming Language, valamint a match referencia-dokumentációját: match Expressions.