Rust में Pattern Matching और Error Handling प्रोग्राम को सुरक्षित और त्रुटिरहित बनाने के लिए अत्यधिक महत्वपूर्ण हैं। Pattern Matching आपको जटिल डेटा संरचनाओं और Enums के साथ काम करने में मदद करता है, जबकि Error Handling के माध्यम से आप runtime errors को सुरक्षित तरीके से संभाल सकते हैं। इस अध्याय में, हम Rust में Pattern Matching के उपयोग और Error Handling की बुनियादी तकनीकों जैसे Result और Option Enums पर चर्चा करेंगे। यह आपको robust और सुरक्षित Rust प्रोग्राम लिखने में मदद करेगा।
Pattern Matching क्या है? (What is Pattern Matching?)
Rust में Pattern Matching एक शक्तिशाली सुविधा है जो आपको किसी वैल्यू के पैटर्न (structure) के आधार पर निर्णय लेने की अनुमति देती है। यह आपको वैरिएंट्स, डेटा संरचनाओं, और विभिन्न प्रकार की वैल्यूज को मैच करने के लिए लचीलापन प्रदान करता है। Rust में Pattern Matching का उपयोग विभिन्न वैरिएंट्स या वैल्यूज की जांच करके उनके आधार पर अलग-अलग कार्य करने के लिए किया जाता है।
Pattern Matching का मुख्य उपयोग Enums, Tuples, Arrays, और अन्य डेटा प्रकारों के साथ किया जाता है। यह Rust के लिए सुरक्षित और expressive error handling का भी मुख्य आधार है, विशेष रूप से Result और Option जैसे Enums के साथ।
Pattern Matching कैसे काम करता है? (How Does Pattern Matching Work?)
Rust में Pattern Matching का सबसे सामान्य तरीका match
स्टेटमेंट के माध्यम से होता है। match
आपको किसी वैल्यू को विभिन्न पैटर्न्स के साथ मिलाने की सुविधा देता है और यह सुनिश्चित करता है कि हर पैटर्न के लिए अलग-अलग कार्य हो सकते हैं। match
Rust में एक एक्सप्रेशन होता है, जो वैल्यू रिटर्न कर सकता है, और इसका उपयोग अक्सर Enum, स्ट्रक्चर या अन्य डेटा के लिए किया जाता है।
Syntax:
match वैरिएबल { पैटर्न1 => { // पैटर्न1 के लिए कार्य }, पैटर्न2 => { // पैटर्न2 के लिए कार्य }, _ => { // अन्य मामलों के लिए (default case) }, }
Pattern Matching का उदाहरण (Example of Pattern Matching)
Enums के साथ Pattern Matching
enum Color { Red, Green, Blue, } fn main() { let my_color = Color::Red; match my_color { Color::Red => println!("रंग लाल है"), Color::Green => println!("रंग हरा है"), Color::Blue => println!("रंग नीला है"), } }
इस उदाहरण में, हमने Color
नामक Enum बनाया है जिसमें तीन वैरिएंट्स हैं: Red
, Green
, और Blue
। match
स्टेटमेंट का उपयोग करके, हम जांचते हैं कि my_color
कौन सा वैरिएंट है, और उसके अनुसार संदेश प्रिंट किया जाता है। अगर वैरिएंट Red
है, तो "रंग लाल है"
प्रिंट किया जाता है।
Default Case (_
) का उपयोग
Pattern Matching में कभी-कभी आप सभी संभावित वैरिएंट्स को कवर नहीं करना चाहते, तो आप default case (_
) का उपयोग कर सकते हैं। यह उन सभी संभावित वैरिएंट्स के लिए काम करता है जो स्पष्ट रूप से परिभाषित नहीं किए गए हैं।
उदाहरण:
fn main() { let my_number = 5; match my_number { 1 => println!("यह 1 है"), 2 => println!("यह 2 है"), _ => println!("यह 1 या 2 नहीं है"), } }
इस उदाहरण में, _
उन सभी संभावित वैरिएंट्स के लिए उपयोग किया गया है जो 1
और 2
नहीं हैं। यह एक default case के रूप में काम करता है।
Complex Data Structures के साथ Pattern Matching
आप Pattern Matching का उपयोग जटिल डेटा संरचनाओं जैसे Tuples, Structs, और Arrays के साथ भी कर सकते हैं। यह आपको विभिन्न प्रकार के डेटा को अधिक आसानी से संभालने की सुविधा देता है।
Tuples के साथ Pattern Matching:
fn main() { let point = (0, 1); match point { (0, y) => println!("x अक्ष पर y = {}", y), (x, 0) => println!("y अक्ष पर x = {}", x), _ => println!("यह किसी भी अक्ष पर नहीं है"), } }
इस उदाहरण में, हम एक ट्यूपल के विभिन्न पैटर्न्स के साथ मैच कर रहे हैं और उसके अनुसार कार्य कर रहे हैं।
Structs के साथ Pattern Matching:
struct Point { x: i32, y: i32, } fn main() { let point = Point { x: 10, y: 0 }; match point { Point { x: 0, y } => println!("x अक्ष पर y = {}", y), Point { x, y: 0 } => println!("y अक्ष पर x = {}", x), _ => println!("यह किसी भी अक्ष पर नहीं है"), } }
यहाँ, Point
Struct का उपयोग करके हम Pattern Matching करते हैं और प्रत्येक पैटर्न के अनुसार प्रिंट करते हैं।
Option Enum के साथ Pattern Matching
Rust का Option
Enum वैल्यू की मौजूदगी या गैर-मौजूदगी को दर्शाता है, और इसका Pattern Matching के साथ उपयोग बहुत ही सामान्य है। Option में दो वैरिएंट्स होते हैं: Some
और None
।
उदाहरण:
fn main() { let some_number = Some(5); match some_number { Some(value) => println!("वैल्यू है: {}", value), None => println!("कोई वैल्यू नहीं है"), } }
इस उदाहरण में, अगर some_number
में वैल्यू होती है, तो Some
वैरिएंट के तहत उसे प्रिंट किया जाता है, और अगर कोई वैल्यू नहीं होती, तो None
वैरिएंट के तहत संदेश दिखाया जाता है।
Result Enum के साथ Pattern Matching
Rust का Result
Enum त्रुटि प्रबंधन (error handling) के लिए बहुत उपयोगी है। इसके दो वैरिएंट्स होते हैं: Ok
(सफलता) और Err
(त्रुटि)। आप Pattern Matching के साथ Result
का उपयोग करके त्रुटियों को आसानी से संभाल सकते हैं।
उदाहरण:
fn divide(a: i32, b: i32) -> Result<i32, String> { if b == 0 { Err(String::from("शून्य से विभाजन संभव नहीं है")) } else { Ok(a / b) } } fn main() { match divide(10, 0) { Ok(result) => println!("परिणाम है: {}", result), Err(e) => println!("त्रुटि: {}", e), } }
इस उदाहरण में, अगर विभाजन संभव है, तो Ok
वैरिएंट परिणाम के साथ रिटर्न किया जाता है, और अगर त्रुटि है (जैसे शून्य से विभाजन), तो Err
वैरिएंट रिटर्न किया जाता है।
Rust में Result और Option Enums (Using Result and Option Enums for Error Handling)
Rust में Error Handling का मुख्य आधार Result
और Option
Enums हैं। ये Enums प्रोग्राम को सुरक्षित और त्रुटिहीन बनाने में मदद करते हैं, क्योंकि ये errors को नियंत्रित और संभालने का एक स्पष्ट और प्रभावी तरीका प्रदान करते हैं।
Rust में गारबेज कलेक्शन नहीं होता, इसलिए error management compile-time पर ही सुनिश्चित किया जाता है। इससे runtime पर होने वाली errors को रोका जा सकता है। आइए, Result
और Option
Enums को विस्तार से समझें और देखें कि उनका उपयोग error handling के लिए कैसे किया जाता है।
Result Enum (त्रुटि और सफलता संभालने के लिए Result)
Result
Enum Rust में सबसे सामान्य तरीका है errors को संभालने का। यह दो वैरिएंट्स (variants) के साथ आता है:
Ok(T)
– जब कोई ऑपरेशन सफल होता है, तो इसका उपयोग वैल्यू वापस करने के लिए किया जाता है।Err(E)
– जब कोई ऑपरेशन विफल होता है, तो इसका उपयोग त्रुटि संदेश या डेटा को वापस करने के लिए किया जाता है।
सिंटैक्स (Syntax):
enum Result<T, E> { Ok(T), Err(E), }
यहां, T
सफल परिणाम का प्रकार है और E
त्रुटि का प्रकार है।
Result का उपयोग (Using Result):
fn divide(a: i32, b: i32) -> Result<i32, String> { if b == 0 { Err(String::from("शून्य से विभाजन संभव नहीं है")) } else { Ok(a / b) } } fn main() { match divide(10, 2) { Ok(result) => println!("परिणाम है: {}", result), Err(e) => println!("त्रुटि: {}", e), } match divide(10, 0) { Ok(result) => println!("परिणाम है: {}", result), Err(e) => println!("त्रुटि: {}", e), } }
इस उदाहरण में, हमने divide
नामक एक फ़ंक्शन परिभाषित किया है जो दो संख्याओं को विभाजित करता है। यदि विभाजन सफल होता है, तो Result
का Ok
वैरिएंट परिणाम लौटाता है, और अगर विभाजन शून्य से हो रहा है, तो Err
वैरिएंट त्रुटि संदेश लौटाता है।
साधारण Result Handling with unwrap
:
अगर आपको error handling की complexity नहीं चाहिए और आपको पूरा यकीन है कि Result
का परिणाम सफल होगा, तो आप unwrap
का उपयोग कर सकते हैं। यह तुरंत Ok
वैल्यू को लौटाता है, और अगर Err
मिलता है, तो प्रोग्राम पैनिक हो जाएगा।
fn main() { let result = divide(10, 2).unwrap(); // सफल होने पर वैल्यू देता है, विफल होने पर प्रोग्राम क्रैश println!("परिणाम है: {}", result); }
Option Enum (वैल्यू के मौजूद होने या न होने के लिए Option)
Option
Enum का उपयोग तब किया जाता है जब किसी वैल्यू की मौजूदगी अनिश्चित हो। यह भी दो वैरिएंट्स के साथ आता है:
Some(T)
– जब वैल्यू मौजूद होती है।None
– जब वैल्यू मौजूद नहीं होती।
सिंटैक्स (Syntax):
enum Option<T> { Some(T), None, }
यह Enum Rust में null pointers की समस्या को हल करने का तरीका है। Rust में null pointers का उपयोग नहीं होता, और उसकी जगह Option Enum से वैल्यू की वैधता को जांचा जाता है।
Option का उपयोग (Using Option):
fn find_item(items: Vec<i32>, target: i32) -> Option<i32> { for item in items { if item == target { return Some(item); } } None } fn main() { let items = vec![1, 2, 3, 4]; match find_item(items, 3) { Some(value) => println!("वस्तु मिली: {}", value), None => println!("वस्तु नहीं मिली"), } }
इस उदाहरण में, हमने एक फ़ंक्शन find_item
परिभाषित किया है, जो किसी सूची में से एक वस्तु खोजने का प्रयास करता है। अगर वस्तु मिलती है, तो Option
का Some
वैरिएंट वैल्यू को लौटाता है, और अगर वस्तु नहीं मिलती, तो None
वैरिएंट लौटाया जाता है।
Option Handling with unwrap
:
अगर आपको यकीन है कि वैल्यू Some
में होगी, तो आप unwrap
का उपयोग कर सकते हैं। अगर वैल्यू None
होगी, तो प्रोग्राम पैनिक हो जाएगा।
fn main() { let items = vec![1, 2, 3, 4]; let item = find_item(items, 2).unwrap(); // वैल्यू मिल जाएगी तो सही, नहीं तो क्रैश println!("मिली हुई वस्तु है: {}", item); }
Error Handling के लिए Result और Option का उपयोग कब करें?
- Result का उपयोग करें:
- जब आप किसी ऑपरेशन के सफल या विफल होने की संभावना को संभालना चाहते हैं।
- जब त्रुटि की स्थिति को विस्तार से वर्णन करना हो, जैसे कि फाइल सिस्टम त्रुटियाँ या नेटवर्क त्रुटियाँ।
- Option का उपयोग करें:
- जब कोई वैल्यू हो भी सकती है और नहीं भी, जैसे किसी सूची में से एक वस्तु का चयन करना।
- जब वैल्यू की गैर-मौजूदगी कोई त्रुटि नहीं है, बल्कि एक वैध स्थिति है।
? ऑपरेटर का उपयोग (Using the ?
Operator)
Rust में ?
ऑपरेटर का उपयोग error handling को सरल और कम जटिल बनाने के लिए किया जाता है। यह ऑपरेटर उस फ़ंक्शन से तुरंत Err
वैरिएंट को लौटा देता है, अगर कोई त्रुटि हो, और अन्यथा वैल्यू को पास करता है।
उदाहरण:
fn divide(a: i32, b: i32) -> Result<i32, String> { if b == 0 { return Err(String::from("शून्य से विभाजन संभव नहीं है")); } Ok(a / b) } fn calculate() -> Result<i32, String> { let result = divide(10, 0)?; Ok(result) } fn main() { match calculate() { Ok(value) => println!("परिणाम है: {}", value), Err(e) => println!("त्रुटि: {}", e), } }
इस उदाहरण में, ?
ऑपरेटर का उपयोग calculate
फ़ंक्शन में किया गया है। यह त्रुटियों को बिना जटिल बनाए हैंडल करता है। अगर divide
फ़ंक्शन Err
लौटाता है, तो calculate
फ़ंक्शन तुरंत उस त्रुटि को लौटाता है।