Ha valaha használtál már más programozási nyelvet (C/C++, Python, Java, Ruby, Lisp stb.), valószínűleg találkoztál már a null vagy a nil fogalmával.
A null vagy a nil használatával jelzik ezek a nyelvek, hogy egy adott változónak nincs értéke.
Ez azonban könnyűvé (és gyakorivá) teszi azt a hibát, hogy véletlenül null értékű változót használunk.
Ahogy sejtheted, abból, hogy olyan függvényt próbálunk meghívni, ami nincs, vagy olyan értéket próbálunk elérni, ami nem létezik, mindenféle hiba és összeomlás származhat.
A null megalkotója odáig ment, hogy a saját „egymilliárd dolláros tévedésének” nevezte.
Option típusHogy elkerüljük ezeket a problémákat, a Cairo változói az Option enum használatával biztonságosan ábrázolhatják az érték hiányát.
Ennek az enumnak két változata van: a None, ami a Cairo megfelelője a nullnak, és a Some(T), ahol a T bármilyen típusú érték lehet.
Így néz ki:
enum Option<T> {
None,
Some(T),
}
Gondolhatsz úgy az Option-ra, mint egy biztonsági rétegre közted és a null-referenciák okozta problémák között, amely megőrzi azok fogalmi hasznosságát.
Option használataEgy változó None-ra állítása meglehetősen egyszerű:
let nothing: Option<u32> = Option::None; // Variable nothing is set to None
Ha viszont azt szeretnéd, hogy az Option típus értéket hordozzon, ezt az értéket nem rendelheted hozzá közvetlenül.
Egy Option típusú változó és mondjuk egy i32 típusú változó nem egyenértékű.
A Some-ot kell használnod:
let wrong_way: Option<i32> = -4; // This will not work
let right_way: Option<i32> = Option::Some(-4); // This will work
let another_right_way = Option::Some(-4); // Compiler infers that this is Option<i32>
Ugyanezen okból a következő sem működik:
let number = 47;
let option_number = Option::Some(15);
let compile_error = number + option_number; // Cannot add an i32 and an Option<i32> - they are of different types
Ahhoz, hogy hozzájuss a Some által tartalmazott értékhez, először ellenőrizned kell, hogy létezik-e:
let mut some_words = Option::Some("choose something to say"); // some_words set to something
match some_words {
Option::Some(str) => println!("Here, we will {}", str),
Option::None => println!("I've got nothing to say"),
} // Prints, "Here, we will choose something to say"
some_words = Option::None; // some_words now set to None
// Exactly the same match block as above
match some_words {
Option::Some(str) => println!("Here, we will {}", str),
Option::None => println!("I've got nothing to say"),
} // Prints, "I've got nothing to say"
A Cairoban a match-en kívül más eszközök is rendelkezésre állnak az Option-ban tárolt értékek ellenőrzésére és elérésére, de a match számodra mostanra ismerős lehet.
Tekintsd ezt annak bemutatásának, hogy miért használ a Cairo Option-t a null-referencia helyett.
A lényeg az, hogy mindenképpen ellenőrizned kell, hogy az Option típusú változó Some-e (ilyenkor nyugodtan kinyerheted és felhasználhatod a benne tárolt értéket), vagy None.
Ellenkező esetben a programod nem fog lefordulni; a fordító megvéd a null-tól.
Egy szerepjáték megvalósításán dolgozol. A játékos karakterét a következő kód írja le:
pub struct Player {
health: u32,
mana: Option<u32>,
level: u32,
}
A játékban a játékosoknak el kell érniük a 10. szintet, hogy feloldják a manapoolt, amit varázslatok megidézésére használhatnak.
Ezt megelőzően a Player manája Option::None.
Két funkción dolgozol ebben a játékban: a feltámasztás mechanikáján és a varázslás mechanikáján.
A revive metódusnak ellenőriznie kell, hogy a Player valóban halott-e (az életereje elérte a 0-t), és ha igen, a metódusnak egy új Player példányt kell visszaadnia 100 életerővel.
A revive metódusnak meg kell őriznie a Player szintjét.
Ha a Player szintje 10 vagy annál nagyobb, akkor 100 manával kell feltámasztani.
Ha a Player szintje 10 alatti, akkor a manája Option::None legyen.
let dead_player = Player { health: 0, mana: Option::None, level: 2 };
dead_player.revive()
// Returns Player { health: 100, mana: Option::None, level: 2 }
Ha a revive metódust olyan Player-re hívják, amelynek az életereje 1 vagy annál több, akkor a metódusnak Option::None-t kell visszaadnia.
let alive_player = Player { health: 1, mana: Option::Some(15), level: 11 };
alive_player.revive()
// Returns Option::None
A cast_spell metódus egy módosítható referenciát kap a Player-re, valamint egy mana_cost paramétert, amely megadja, mennyi manába kerül a varázslat.
Visszaadja, hogy a megidézett varázslat mennyi sebzést okoz.
Ha a játékosnak nincs hozzáférése manapoolhoz, akkor a varázslat megidézésekor az életereje a varázslat manaköltségével csökken. A visszaadott sebzésnek 0-nak kell lennie.
let not_a_wizard_yet = Player { health: 79, mana: Option::None, level: 9 };
assert_eq!(not_a_wizard_yet.cast_spell(5), 0)
assert_eq!(not_a_wizard_yet.health, 74);
assert_eq!(not_a_wizard_yet.mana, Option::None);
Ha a játékosnak van manapoolja, de nincs elég manája, a metódus ne módosítsa a poolt. A visszaadott sebzésnek 0-nak kell lennie.
let low_mana_wizard = Player { health: 93, mana: Option::Some(3), level: 12 };
assert_eq!(low_mana_wizard.cast_spell(10), 0);
assert_eq!(low_mana_wizard.health, 93);
assert_eq!(low_mana_wizard.mana, Option::Some(3));
Egyébként a varázslat sikeresen megidéződik.
A mana_cost levonódik a Player manapooljából.
A visszaadott sebzésnek a varázslat manaköltségének kétszeresének kell lennie.
let wizard = Player { health: 123, mana: Option::Some(30), level: 18 };
assert_eq!(wizard.cast_spell(10), 20);
assert_eq!(wizard.health, 123);
assert_eq!(wizard.mana, Option::Some(20));
Jó szórakozást!
Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) Cairo nyelvet 25 fogalom68 feladat segítségével, valódi emberi mentorálással, mindez ingyen.