अगर आपने पहले कभी कोई दूसरी प्रोग्रामिंग भाषा (C/C++, Python, Java, Ruby, Lisp, वगैरह) इस्तेमाल की है, तो बहुत संभव है कि आपका सामना पहले भी null या nil से हुआ हो।
ये भाषाएँ null या nil का इस्तेमाल करके बताती हैं कि किसी खास वेरिएबल में कोई वैल्यू नहीं है।
लेकिन इसकी वजह से गलती से किसी null वेरिएबल का इस्तेमाल कर देना बहुत आसान और आम गलती बन जाती है।
जैसा कि आप सोच सकते हैं, ऐसे फंक्शन को कॉल करने की कोशिश करना जो मौजूद ही नहीं है, या ऐसी वैल्यू तक पहुँचने की कोशिश करना जो है ही नहीं, कई तरह के बग और क्रैश पैदा कर सकती है।
null बनाने वाले ने तो इसे अपनी 'अरबों डॉलर की गलती' तक कह दिया।
Option टाइपइन समस्याओं से बचने के लिए Cairo के वेरिएबलो Option इनम का इस्तेमाल कर सकते हैं, जो वैल्यू न होने की स्थिति को सुरक्षित तरीके से दर्शाता है।
इस इनम के दो वेरिएंट होते हैं: None, जो Cairo में null जैसा ही है; और Some(T), जहाँ T किसी भी टाइप की एक वैल्यू हो सकती है।
यह कुछ ऐसा दिखता है:
enum Option<T> {
None,
Some(T),
}
Option को आप अपने और उन समस्याओं के बीच सुरक्षा की एक परत की तरह सोच सकते हैं जो नल रेफरेंस पैदा कर सकते हैं, और फिर भी उनकी उपयोगिता बनी रहती है।
Option का इस्तेमालकिसी वेरिएबल को None पर सेट करना काफी आसान है:
let nothing: Option<u32> = Option::None; // Variable nothing is set to None
लेकिन अगर आप चाहते हैं कि Option टाइप कोई वैल्यू अपने अंदर रखे, तो आप उस वैल्यू को सीधे असाइन नहीं कर सकते।
एक Option टाइप का वेरिएबल और, मान लीजिए, एक i32 टाइप का वेरिएबल एक जैसे नहीं होते।
आपको Some का इस्तेमाल करना होगा:
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>
इसी वजह से यह भी काम नहीं करेगा:
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
Some के अंदर रखी वैल्यू पाने के लिए पहले यह जाँचना ज़रूरी है कि वह मौजूद है:
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"
Option के अंदर रखी वैल्यू को जाँचने और उस तक पहुँचने के लिए Cairo में match के अलावा दूसरे टूल भी हैं, लेकिन match तो अब आपके लिए परिचित होना चाहिए।
इसे इस बात का उदाहरण मानिए कि Cairo नल रेफरेंस की जगह Option क्यों इस्तेमाल करता है।
बात यह है कि आपको हर हाल में जाँचना ही होगा कि Option वेरिएबल Some है या नहीं (ऐसी स्थिति में आप आगे बढ़कर उसके अंदर रखी वैल्यू निकालकर इस्तेमाल कर सकते हैं), या None।
वरना आपका प्रोग्राम कंपाइल नहीं होगा; कंपाइलर आपको null से बचाए रखता है।
आप एक रोल-प्लेइंग गेम बनाने पर काम कर रहे हैं। खिलाड़ी का पात्र इस तरह दर्शाया गया है:
pub struct Player {
health: u32,
mana: Option<u32>,
level: u32,
}
इस गेम में खिलाड़ियों को स्पेल चलाने के लिए इस्तेमाल होने वाला मैना पूल अनलॉक करने से पहले लेवल 10 तक पहुँचना होता है।
उससे पहले प्लेयर का मैना Option::None होता है।
इस गेम में आप दो चीज़ों पर काम कर रहे हैं: रिवाइव करने का तरीका और स्पेल कास्टिंग का तरीका।
revive मेथड को यह जाँचना चाहिए कि प्लेयर वाकई मर चुका है (उसकी हेल्थ 0 तक पहुँच गई है), और अगर ऐसा है, तो मेथड को 100 हेल्थ वाला एक नया प्लेयर इंस्टेंस लौटाना चाहिए।
revive मेथड को प्लेयर का लेवल वैसा ही रखना चाहिए।
अगर प्लेयर का लेवल 10 या उससे अधिक है, तो उसे 100 मैना के साथ रिवाइव किया जाना चाहिए।
अगर प्लेयर का लेवल 10 से कम है, तो उसका मैना Option::None होना चाहिए।
let dead_player = Player { health: 0, mana: Option::None, level: 2 };
dead_player.revive()
// Returns Player { health: 100, mana: Option::None, level: 2 }
अगर revive मेथड ऐसे प्लेयर पर कॉल किया जाता है जिसकी हेल्थ 1 या उससे अधिक है, तो मेथड को Option::None लौटाना चाहिए।
let alive_player = Player { health: 1, mana: Option::Some(15), level: 11 };
alive_player.revive()
// Returns Option::None
cast_spell मेथड प्लेयर का म्यूटेबल रेफरेंस लेता है और साथ में एक mana_cost पैरामीटर भी, जो बताता है कि उस स्पेल पर कितना मैना लगता है।
यह उस डैमेज की मात्रा लौटाता है जो चला गया स्पेल करता है।
अगर खिलाड़ी के पास मैना पूल नहीं है, तो स्पेल चलाने की कोशिश करने पर उसकी हेल्थ उतनी कम हो जानी चाहिए जितना मैना वह स्पेल माँगता है। लौटाया गया डैमेज 0 होना चाहिए।
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);
अगर खिलाड़ी के पास मैना पूल है लेकिन मैना कम है, तो मेथड को पूल पर कोई असर नहीं डालना चाहिए। लौटाया गया डैमेज 0 होना चाहिए।
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));
वरना, स्पेल सफलतापूर्वक चल जाता है।
प्लेयर के मैना पूल से mana_cost घटा दिया जाना चाहिए।
लौटाया गया डैमेज स्पेल पर लगने वाले मैना से दुगुना होना चाहिए।
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));
मज़े कीजिए!
Exercism पर साइन अप कीजिए और Cairo को 25 कॉन्सेप्ट68 अभ्यास तथा असली इंसानों से मिलने वाली मेंटरिंग के साथ सीखिए और उसमें महारत हासिल कीजिए, वह भी बिल्कुल मुफ्त।