Rust में Structs और Enums आपको जटिल डेटा प्रकारों को परिभाषित करने और संगठित करने की सुविधा देते हैं। Structs का उपयोग तब किया जाता है जब आपको एक से अधिक वैल्यू को एक साथ रखने की आवश्यकता होती है, जबकि Enums आपको कई संबंधित वैल्यूज़ में से किसी एक को चुनने का विकल्प देते हैं। इस अध्याय में, हम देखेंगे कि Rust में Structs और Enums कैसे काम करते हैं, इन्हें कैसे परिभाषित किया जाता है, और वे आपके कोड को अधिक पढ़ने योग्य और संगठित बनाने में कैसे मदद करते हैं।
Structs क्या हैं? (What are Structs?)
Rust में Structs का उपयोग तब किया जाता है जब आपको एक से अधिक डेटा आइटम (वैल्यू) को एक साथ संगठित करने की आवश्यकता होती है। Structs आपको अलग-अलग प्रकार के डेटा को एक ही जगह पर रखने की सुविधा देते हैं। आप इसे एक कंटेनर की तरह समझ सकते हैं, जो कई प्रकार की वैल्यूज़ को एकत्रित करके एक संगठित संरचना में रखता है।
Structs का उपयोग प्रोग्राम में जटिल डेटा को मॉडल करने के लिए किया जाता है। उदाहरण के लिए, यदि आपको किसी व्यक्ति की जानकारी जैसे नाम, उम्र, और पता को एक साथ संगठित करना हो, तो Structs का उपयोग सबसे अच्छा तरीका होता है।
Structs का परिचय (Introduction to Structs)
Structs एक प्रकार के कस्टम डेटा टाइप होते हैं, जिसमें आप विभिन्न प्रकार के डेटा को समूहबद्ध कर सकते हैं। Rust में Structs को तीन मुख्य प्रकारों में विभाजित किया जा सकता है:
- Regular Struct (सामान्य Struct): यह Struct नामित फ़ील्ड्स (fields) के साथ होता है।
- Tuple Struct: यह Struct ट्यूपल्स के रूप में होता है, जिसमें फील्ड्स को नाम नहीं दिए जाते।
- Unit Struct: यह Struct नाम के लिए खाली Struct होता है, जिसमें कोई डेटा नहीं होता।
Structs का उदाहरण (Example of Structs)
सामान्य Struct (Regular Struct)
एक सामान्य Struct में, हर फ़ील्ड का एक नाम और एक प्रकार होता है। आप इस प्रकार के Struct का उपयोग तब करते हैं जब आपको किसी विशेष प्रकार के डेटा को अधिक संगठित तरीके से रखना हो।
struct Person { name: String, age: u8, address: String, } fn main() { let person1 = Person { name: String::from("राज"), age: 30, address: String::from("दिल्ली"), }; println!("नाम: {}, उम्र: {}, पता: {}", person1.name, person1.age, person1.address); }
इस उदाहरण में, Person
Struct में तीन फील्ड्स हैं: name
, age
, और address
, जो एक व्यक्ति की जानकारी को संगठित रूप से स्टोर करती हैं। person1
नामक एक वैरिएबल को इस Struct के रूप में घोषित किया गया है, और हमने उसमें व्यक्ति की जानकारी असाइन की है।
Tuple Struct (ट्यूपल Struct)
Tuple Structs में नामित फील्ड्स नहीं होते, बल्कि केवल फील्ड्स के प्रकार होते हैं। इसे उसी प्रकार से एक्सेस किया जाता है जैसे कि ट्यूपल्स को एक्सेस किया जाता है।
struct Color(i32, i32, i32); fn main() { let black = Color(0, 0, 0); println!("रंग: ({}, {}, {})", black.0, black.1, black.2); }
यहाँ Color
नामक एक Tuple Struct है, जिसमें तीन i32
प्रकार की वैल्यूज हैं। Tuple Structs में फील्ड्स को नाम की बजाय इंडेक्स द्वारा एक्सेस किया जाता है, जैसे कि black.0
, black.1
, आदि।
Unit Struct (यूनिट Struct)
Unit Structs में कोई फ़ील्ड नहीं होती, वे सिर्फ़ नाम होते हैं। इसका उपयोग आमतौर पर तब किया जाता है जब आपको किसी प्रकार के डेटा की आवश्यकता नहीं होती, लेकिन आप किसी Struct को ट्रैक करना चाहते हैं।
struct Unit; fn main() { let u = Unit; println!("Unit Struct बनाया गया है!"); }
यहाँ, Unit
Struct कोई डेटा स्टोर नहीं करता, लेकिन इसे आप नाम के रूप में उपयोग कर सकते हैं।
Structs क्यों उपयोग करें? (Why Use Structs?)
- डेटा को व्यवस्थित करना (Organizing Data):
- Structs का उपयोग करने से आपका डेटा साफ़ और व्यवस्थित रहता है। आप संबंधित डेटा आइटम्स को एक ही Struct में समेकित कर सकते हैं।
- पढ़ने योग्य कोड (Readable Code):
- Structs आपके कोड को पढ़ने योग्य बनाते हैं। आप डेटा आइटम्स को नाम देकर संदर्भित कर सकते हैं, जो कोड को अधिक स्पष्ट और समझने में आसान बनाता है।
- डेटा सुरक्षा (Data Safety):
- Structs का उपयोग डेटा के प्रकारों और मूल्यों को सुरक्षित रूप से संभालने में मदद करता है, क्योंकि आप प्रत्येक फ़ील्ड के प्रकार को सख्ती से परिभाषित कर सकते हैं।
Enums का उपयोग (When and Why to Use Enums)
Rust में Enums (Enumerations) का उपयोग उन स्थितियों में किया जाता है, जब किसी वैल्यू को कई संभावित विकल्पों (variants) में से एक होना चाहिए। Enums आपको एक ही डेटा प्रकार के तहत कई संबंधित विकल्पों को परिभाषित करने की सुविधा देते हैं। इस प्रकार की संरचना जटिल और असंगत डेटा को सुरक्षित रूप से संभालने में मदद करती है।
Enums का उपयोग तब किया जाता है जब हमें विकल्पों या वैरिएंट्स के एक निश्चित सेट में से किसी एक को चुनने की आवश्यकता होती है। उदाहरण के लिए, आप किसी गेम में दिशा को चार संभावित विकल्पों में से एक (उत्तर, दक्षिण, पूर्व, पश्चिम) के रूप में देख सकते हैं, या किसी ऑपरेशन के परिणाम को “सफलता” या “त्रुटि” के रूप में प्रकट कर सकते हैं।
Enums का परिचय (Introduction to Enums)
Enums आपको डेटा के कुछ संभावित विकल्पों को एक साथ रखने और उनका एक नाम देने की सुविधा देते हैं। प्रत्येक वैरिएंट (variant) Enums के तहत परिभाषित होता है, और एक ही समय में एक Enum केवल एक वैरिएंट हो सकता है। यह आपको अधिक सुरक्षित और स्पष्ट कोड लिखने में मदद करता है, जहाँ प्रत्येक वैरिएंट के साथ संबंधित डेटा भी हो सकता है।
Enums का उपयोग कब करें? (When to Use Enums)
- विभिन्न विकल्पों को परिभाषित करने के लिए (Defining Multiple Variants):
- जब आपको एक वैल्यू को कई संबंधित विकल्पों (variants) में से एक होना हो। उदाहरण के लिए, किसी वाहन के प्रकार के लिए Enum के तहत
Car
,Bike
,Bus
जैसे विकल्प हो सकते हैं।
- जब आपको एक वैल्यू को कई संबंधित विकल्पों (variants) में से एक होना हो। उदाहरण के लिए, किसी वाहन के प्रकार के लिए Enum के तहत
- जटिल डेटा संरचनाओं के लिए (Handling Complex Data Structures):
- Enums तब उपयोगी होते हैं जब आपको विभिन्न वैरिएंट्स के साथ अतिरिक्त डेटा संग्रहीत करने की आवश्यकता होती है। आप प्रत्येक वैरिएंट के साथ विभिन्न प्रकार के डेटा जोड़ सकते हैं।
- कार्य की सफलता और त्रुटि प्रबंधन के लिए (Managing Success and Error Handling):
- Rust में, Result और Option जैसे Enums का उपयोग सफलता (success) और त्रुटियों (errors) को संभालने के लिए किया जाता है। आप अपनी खुद की Enums बनाकर भी ऐसा कर सकते हैं।
Enums का उपयोग क्यों करें? (Why Use Enums)
- स्पष्टता और सुरक्षा (Clarity and Safety):
- Enums आपके कोड को अधिक सुरक्षित बनाते हैं, क्योंकि यह सुनिश्चित करता है कि केवल सही वैरिएंट का उपयोग हो सकता है। यह डेवलपर्स को संभावित विकल्पों की सूची से ही चुनने की सुविधा देता है, जिससे गलत डेटा का उपयोग कम होता है।
- संगठित डेटा (Organized Data):
- Enums जटिल डेटा को बेहतर ढंग से संगठित करने में मदद करते हैं। यह आपको कई संबंधित विकल्पों को एक जगह पर रखने की सुविधा देता है, जिससे कोड पढ़ने में आसान हो जाता है।
- सहायक पैटर्न मैचिंग (Pattern Matching):
- Rust में, Enums का सबसे बड़ा फायदा यह है कि आप उनके साथ pattern matching कर सकते हैं। यह आपको प्रत्येक वैरिएंट के लिए अलग-अलग कार्य करने की सुविधा देता है, जो कोड को सुरक्षित और स्पष्ट बनाता है।
Enums का उदाहरण (Example of Enums)
साधारण Enum (Simple Enum)
enum Direction { North, South, East, West, } fn main() { let my_direction = Direction::North; match my_direction { Direction::North => println!("उत्तर की ओर जा रहे हैं"), Direction::South => println!("दक्षिण की ओर जा रहे हैं"), Direction::East => println!("पूर्व की ओर जा रहे हैं"), Direction::West => println!("पश्चिम की ओर जा रहे हैं"), } }
इस उदाहरण में, Direction
नाम का Enum चार संभावित विकल्पों (North, South, East, West) को परिभाषित करता है। हम match
का उपयोग करके प्रत्येक दिशा के लिए एक अलग क्रिया कर सकते हैं।
वैरिएंट्स के साथ डेटा (Variants with Data)
Enums के प्रत्येक वैरिएंट के साथ आप डेटा भी जोड़ सकते हैं, जिससे आप अधिक जटिल डेटा संरचनाओं को संभाल सकते हैं।
enum Vehicle { Car(String), Bike(String), Bus(String, u32), // बस का प्रकार और यात्री क्षमता } fn main() { let my_vehicle = Vehicle::Bus(String::from("City Bus"), 50); match my_vehicle { Vehicle::Car(name) => println!("यह एक कार है: {}", name), Vehicle::Bike(name) => println!("यह एक बाइक है: {}", name), Vehicle::Bus(name, capacity) => println!("{} में {} यात्री जा सकते हैं", name, capacity), } }
इस उदाहरण में, Vehicle
Enum के विभिन्न वैरिएंट्स हैं (Car, Bike, Bus), और प्रत्येक वैरिएंट में अलग-अलग प्रकार का डेटा भी शामिल है। Bus
वैरिएंट में एक string (बस का नाम) और एक integer (यात्री क्षमता) शामिल हैं।
Result और Option Enums का उपयोग (Using Result and Option Enums)
Rust में पहले से परिभाषित Enums, जैसे कि Result और Option, बहुत शक्तिशाली हैं और इन्हें त्रुटि प्रबंधन और वैल्यू की वैधता की जाँच के लिए उपयोग किया जाता है।
Result Enum:
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), } }
Option Enum:
Option का उपयोग तब किया जाता है जब कोई वैल्यू या तो हो सकती है या नहीं हो सकती (None)।
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!("वस्तु नहीं मिली"), } }
Enums बनाम Structs (Enums vs Structs)
- Enums:
- जब आपको किसी वैल्यू को संभावित वैरिएंट्स में से एक चुनने की आवश्यकता हो।
- जब वैल्यू में कई वैरिएंट्स के साथ अलग-अलग प्रकार का डेटा हो।
- Structs:
- जब आपको एक से अधिक वैल्यू को एक साथ रखने की आवश्यकता हो, लेकिन उन सभी का डेटा प्रकार समान हो या उन्हें एक साथ संगठित करना हो।
Pattern Matching के साथ Enums का उपयोग (Using Enums with Pattern Matching)
Rust में Enums का सबसे बड़ा फायदा यह है कि इन्हें आप pattern matching के साथ आसानी से उपयोग कर सकते हैं। Pattern matching आपको यह जांचने की सुविधा देता है कि किसी Enum का कौन-सा वैरिएंट (variant) इस्तेमाल हो रहा है और उस वैरिएंट के साथ संबंधित डेटा को कैसे एक्सेस करना है। यह Rust में निर्णय लेने (decision-making) और जटिल डेटा संरचनाओं को संभालने का एक शक्तिशाली और सुरक्षित तरीका है।
Pattern matching को Rust में match
और if let
जैसे कीवर्ड्स के माध्यम से लागू किया जाता है। ये कीवर्ड्स आपको हर संभावित वैरिएंट के लिए विभिन्न कार्य करने की अनुमति देते हैं, जिससे कोड पढ़ने में सरल और सुरक्षित हो जाता है।
Pattern Matching के साथ Enums का उपयोग (Using Pattern Matching with Enums)
Pattern matching का सबसे सामान्य तरीका match
स्टेटमेंट है। यह हर वैरिएंट को चेक करता है और संबंधित कोड ब्लॉक को निष्पादित करता है।
उदाहरण:
enum Vehicle { Car(String), Bike(String), Bus(String, u32), // बस का नाम और यात्री क्षमता } fn main() { let my_vehicle = Vehicle::Bus(String::from("City Bus"), 50); match my_vehicle { Vehicle::Car(name) => println!("यह एक कार है: {}", name), Vehicle::Bike(name) => println!("यह एक बाइक है: {}", name), Vehicle::Bus(name, capacity) => println!("{} में {} यात्री जा सकते हैं", name, capacity), } }
इस उदाहरण में, Vehicle
Enum के तीन वैरिएंट्स (Car, Bike, Bus) हैं। हम match
स्टेटमेंट का उपयोग करके यह जांच रहे हैं कि कौन-सा वैरिएंट उपयोग हो रहा है और उसके अनुसार कार्य कर रहे हैं। अगर वैरिएंट Bus
है, तो हम name
और capacity
(यात्री क्षमता) को एक्सेस करके जानकारी प्रदर्शित कर रहे हैं।
Option Enum के साथ Pattern Matching
Rust का Option
Enum बहुत ही सामान्य और उपयोगी है, जो वैल्यू की मौजूदगी या गैर-मौजूदगी को दर्शाता है। इसे भी pattern matching के साथ प्रभावी रूप से इस्तेमाल किया जाता है।
उदाहरण:
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
फ़ंक्शन एक वैल्यू ढूँढने का प्रयास करता है और यदि वैल्यू मिलती है, तो Some
वैरिएंट के साथ उस वैल्यू को लौटाता है। अगर वैल्यू नहीं मिलती, तो None
वैरिएंट लौटाया जाता है। Pattern matching का उपयोग करके, हम यह जाँचते हैं कि क्या वैल्यू मिली और उसके अनुसार कार्य करते हैं।
Result Enum के साथ Pattern Matching
Result
Enum का उपयोग Rust में त्रुटियों (errors) और सफलताओं (success) को संभालने के लिए किया जाता है। यह दो वैरिएंट्स के साथ आता है: Ok
(सफलता) और Err
(त्रुटि)। इसका pattern matching के साथ उपयोग Rust में त्रुटि प्रबंधन को सरल और सुरक्षित बनाता है।
उदाहरण:
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), } }
इस उदाहरण में, divide
फ़ंक्शन एक integer को दूसरे से विभाजित करता है। अगर विभाजक (divider) शून्य होता है, तो यह एक Err
लौटाता है, अन्यथा यह Ok
के साथ परिणाम लौटाता है। Pattern matching का उपयोग करके, हम यह सुनिश्चित कर सकते हैं कि यदि कोई त्रुटि होती है तो वह संभाली जाए।
If Let के साथ Pattern Matching
if let
एक सरल और पठनीय तरीका है pattern matching के लिए, जब आपको केवल एक ही वैरिएंट को संभालना हो। यह pattern matching को कम जटिल बनाता है, खासकर जब आपको सिर्फ एक वैरिएंट की जांच करनी हो।
उदाहरण:
fn main() { let my_vehicle = Vehicle::Bike(String::from("Mountain Bike")); if let Vehicle::Bike(name) = my_vehicle { println!("यह एक बाइक है: {}", name); } else { println!("यह बाइक नहीं है"); } }
इस उदाहरण में, हमने if let
का उपयोग किया है ताकि my_vehicle
को सीधे Bike
वैरिएंट से मिलान किया जा सके। अगर my_vehicle
एक Bike
है, तो हम उसका नाम प्रिंट करते हैं, अन्यथा कोई और संदेश देते हैं।
Enums और Pattern Matching का उपयोग कब करें? (When to Use Enums and Pattern Matching)
- विभिन्न वैरिएंट्स को संभालने के लिए:
- जब आपको एक वैरिएंट में से किसी एक को चुनना हो और हर वैरिएंट के लिए अलग-अलग कार्य करना हो, तब Enums और pattern matching का उपयोग करें।
- त्रुटि प्रबंधन (Error Handling):
- Rust के
Result
औरOption
Enums के साथ pattern matching का उपयोग त्रुटियों को सुरक्षित और संरचित तरीके से संभालने के लिए किया जाता है।
- Rust के
- डाटा वैल्यूज और प्रकारों को परिभाषित करने के लिए:
- जब आपके डेटा के साथ कई प्रकार की वैल्यूज हों, जिन्हें आप एक Enum के अंदर व्यवस्थित करना चाहते हों, तब pattern matching उन्हें अलग-अलग तरीके से संभालने का बेहतरीन तरीका है।
Rust में Enums और Pattern Matching का उपयोग प्रोग्राम को सुरक्षित और संगठित बनाता है। Pattern matching के द्वारा आप Enums के हर वैरिएंट के लिए अलग-अलग कार्य कर सकते हैं, जिससे जटिल डेटा संरचनाओं को संभालना और भी सरल हो जाता है। चाहे आप वैरिएंट्स के साथ अतिरिक्त डेटा को संभाल रहे हों या त्रुटि प्रबंधन कर रहे हों, pattern matching Rust के कोड को पठनीय और सुरक्षित बनाता है।