Rust में Pattern Matching – Pattern Matching in Rust

Rust में Pattern Matching  – Pattern Matching in Rust

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, और Bluematch स्टेटमेंट का उपयोग करके, हम जांचते हैं कि 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 में Pattern Matching डेटा संरचनाओं और वैरिएंट्स को सुरक्षित और स्पष्ट रूप से संभालने का एक शक्तिशाली तरीका है। यह आपको Enums, Structs, Tuples, और अन्य जटिल डेटा प्रकारों के साथ सुरक्षित निर्णय लेने की सुविधा देता है। Pattern Matching का सबसे बड़ा फायदा यह है कि आप अपने कोड को सुरक्षित और त्रुटिहीन बना सकते हैं, खासकर जब आप Option और Result जैसे Enums का उपयोग करते हैं। Pattern Matching का सही उपयोग करने से Rust कोड अधिक लचीला, सुरक्षित और पठनीय बनता है।

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) के साथ आता है:

  1. Ok(T) – जब कोई ऑपरेशन सफल होता है, तो इसका उपयोग वैल्यू वापस करने के लिए किया जाता है।
  2. 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 का उपयोग तब किया जाता है जब किसी वैल्यू की मौजूदगी अनिश्चित हो। यह भी दो वैरिएंट्स के साथ आता है:

  1. Some(T) – जब वैल्यू मौजूद होती है।
  2. 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 का उपयोग कब करें?

  1. Result का उपयोग करें:
    • जब आप किसी ऑपरेशन के सफल या विफल होने की संभावना को संभालना चाहते हैं।
    • जब त्रुटि की स्थिति को विस्तार से वर्णन करना हो, जैसे कि फाइल सिस्टम त्रुटियाँ या नेटवर्क त्रुटियाँ।
  2. 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 फ़ंक्शन तुरंत उस त्रुटि को लौटाता है।

Rust में Result और Option Enums error handling और वैल्यू के मौजूद होने/न होने को संभालने के लिए अत्यधिक उपयोगी उपकरण हैं। ये Enums compile-time पर errors को पकड़ने में मदद करते हैं और runtime पर होने वाली त्रुटियों को कम करते हैं। इनके साथ match, unwrap, और ? ऑपरेटर का सही उपयोग करके आप अपने प्रोग्राम को अधिक सुरक्षित, लचीला, और त्रुटिहीन बना सकते हैं।



Index