परिचय: इटरेटर क्यों नहीं रुकना चाहता?
\n\nकल्पना करें: आप Rust में एक प्रोग्राम लिख रहे हैं जो संख्याओं की सूची को प्रोसेस करता है — स्ट्रिंग्स को पार्स करता है, फ़ाइलें पढ़ता है, कुछ रूपांतरण करता है। आप प्रत्येक तत्व पर जाने के लिए Iterator::map का उपयोग कर रहे हैं। और अचानक — बैम! — एक तत्व Result::Err त्रुटि में बदल जाता है। तार्किक रूप से, पुनरावृत्ति को तुरंत रोक देना चाहिए और इस त्रुटि को ऊपर लौटा देना चाहिए। लेकिन map बस काम करता रहता है, जैसे कुछ हुआ ही न हो। क्यों? और सबसे महत्वपूर्ण बात — इसे कैसे ठीक करें?
यह प्रश्न Rust में शुरुआती लोगों के बीच Stack Overflow पर सबसे लोकप्रिय प्रश्नों में से एक है। और यह कोई संयोग नहीं है। Rust में त्रुटि प्रबंधन तंत्र लचीले हैं, लेकिन उनके लिए एक सचेत दृष्टिकोण की आवश्यकता होती है। इस लेख में, हम समस्या को गहराई से समझेंगे, कई समाधानों पर विचार करेंगे और सबसे सुंदर समाधान चुनेंगे। चलिए शुरू करते हैं!
\n\nसमस्या: map नहीं रुकता
\n\nचलिए एक क्लासिक उदाहरण से शुरू करते हैं। मान लीजिए, हमारे पास स्ट्रिंग्स का एक वेक्टर है जिसे हम संख्याओं में बदलना चाहते हैं:
\n\nfn main() {\n let inputs = vec!["42", "hello", "100"];\n\n let result: Vec<Result<i32, std::num::ParseIntError>> =\n inputs.iter().map(|s| s.parse::<i32>()).collect();\n\n println!("{:?}", result);\n}\n\nयह कोड कंपाइल होगा और चलेगा। लेकिन हमें क्या मिलेगा? Result का एक वेक्टर: [Ok(42), Err(ParseIntError), Ok(100)]। पुनरावृत्ति "hello" पर नहीं रुकी, बल्कि आगे बढ़ती रही। कई कार्यों के लिए यह सामान्य है — आप सभी त्रुटियों को इकट्ठा करना चाहते हैं। लेकिन क्या होगा यदि आपको पहले अपवाद की आवश्यकता है? उदाहरण के लिए, कॉन्फ़िगरेशन लोड करते समय: यदि एक पैरामीटर गलत है, तो आगे जाँच करना व्यर्थ है।
समस्या यह है कि map एक आलसी रूपांतरण है। यह नहीं जानता कि "रुकना" क्या होता है। यह बस कहता है: "मुझे एक तत्व दो, मैं फ़ंक्शन लागू करूँगा, परिणाम लौटाऊँगा।" रुकने और जारी रखने का सारा नियंत्रण इटरेटर के उपभोक्ता (collect, for, fold आदि) पर निर्भर करता है। और Vec के लिए collect बस अंत तक जाता है।
समाधान 1: FromIterator के साथ collect (सबसे लोकप्रिय)
\n\nStack Overflow पर सबसे आम उत्तर collect::<Result<Vec<_>, _>>() का उपयोग करना है। Rust जानता है कि Result के इटरेटर को एक ही Result में अंदर वेक्टर के साथ इकट्ठा किया जा सकता है। यदि Err मिलता है, तो संग्रह रुक जाता है और वह त्रुटि लौटा दी जाती है।
fn main() -> Result<(), std::num::ParseIntError> {\n let inputs = vec!["42", "hello", "100"];\n\n let result: Vec<i32> =\n inputs.iter()\n .map(|s| s.parse::<i32>())\n .collect::<Result<Vec<_>, _>>()?; // <-- जादू!\n\n println!("{:?}", result);\n Ok(())\n}\n\nयह कैसे काम करता है? प्रकार Result<Vec<i32>, ParseIntError> ट्रेट FromIterator को लागू करता है। जब collect Err देखता है, तो यह शेष तत्वों को प्रोसेस किए बिना तुरंत उसे लौटा देता है। यदि सब कुछ ठीक है — आपको Ok(वेक्टर) मिलता है। यह संक्षिप्त, मुहावरेदार और कुशल है।
नुकसान: आप इस बारे में जानकारी खो देते हैं कि किस तत्व पर त्रुटि हुई (जब तक कि आप इसे अपने प्रकार में लपेट न दें)। लेकिन त्वरित स्क्रिप्ट के लिए — यह सबसे अच्छा है।
\n\nसमाधान 2: त्रुटियों को अनदेखा करने के लिए filter_map
\n\nकभी-कभी आप रुकना नहीं चाहते, बल्कि केवल असफल तत्वों को अनदेखा करना चाहते हैं। उदाहरण के लिए, केवल मान्य स्ट्रिंग्स गिनना। इसके लिए filter_map एकदम सही है:
fn main() {\n let inputs = vec!["42", "hello", "100", "abc", "7"];\n\n let numbers: Vec<i32> = inputs.iter()\n .filter_map(|s| s.parse::<i32>().ok()) // .ok() Err को None में बदलता है\n .collect();\n\n println!("{:?}", numbers); // [42, 100, 7]\n}\n\nfilter_map एक फ़ंक्शन लेता है जो