HeadlinesBriefing favicon HeadlinesBriefing.com

रस्ट में पार्स न करें सत्यापित करें: गैर-खाली वेक्टर

Hacker News •
×

कई प्रोग्रामरों की तरह, मुझे एलेक्सिस किंग का 'पार्स न करें सत्यापित करें' लेख बहुत दिलचस्प लगता है, क्योंकि यह एक ऐसे पैटर्न को नाम देता है जो मुझे परिचित और महत्वपूर्ण लगता है - जिसे मैंने पहले देखा और इस्तेमाल किया है, लेकिन इसे स्पष्ट रूप से नाम नहीं दिया है। यह लेख 'पार्स न करें सत्यापित करें' पैटर्न की समीक्षा है, जिसे रस्ट प्रोग्रामिंग भाषा में लागू किया गया है (मूल लेख में हैस्केल का उपयोग किया गया है)। मुझे विशेष रूप से रस्ट स्टैंडर्ड लाइब्रेरी और अन्य प्रसिद्ध प्रोजेक्ट्स में इस पैटर्न के शैक्षिक उदाहरण खोजने में दिलचस्पी थी। मूल लेख को दोहराए बिना (कृपया पहले इसे पढ़ें!), यहाँ इसका सार है। वर्जिन वेक्टर को देखें; इसका पहला मेथड Option<&T> वापस करता है। क्यों? क्योंकि वेक्टर में कोई तत्व होने की गारंटी नहीं है, इसलिए यदि first को एक खाली वेक्टर पर कॉल किया जाता है तो क्या करें? इस मामले में Option वापस करना रस्ट में एक आइडियोमेटिक तरीका है [1], जिसमें Option वापस करने वाले फंक्शन्स के परिणाम को स्वीकार करने और अगले कदम का फैसला करने के लिए सुविधाजनक सिंटैक्स शुगर है। तो समस्या क्या है? मान लीजिए कि हमारे पास एक फंक्शन है जो एनवायरनमेंट वेरिएबल से कुछ कॉन्फ़िगरेशन पाथ्स को पढ़ता है, जबकि यह सुनिश्चित करता है कि सूची खाली नहीं हो सकती: use anyhow::{Result, ensure}; fn get_configuration_directories() -> Result<Vec<Path Buf>> { let value = env::var("CONFIG_DIRS").context("could not read CONFIG_DIRS")?; let directories: Vec<Path Buf> = value.split(',').map(str::trim).map(Path Buf::from).collect(); ensure!(!directories.is_empty(), "empty CONFIG_DIRS"); Ok(directories) } अब तक, सब ठीक है। अब इस फंक्शन के एक सामान्य उपयोग को देखें: fn main() -> Result<()> { let config_dirs = get_configuration_directories()?; match config_dirs.first() { Some(cache_dir) => initialize_cache(cache_dir), None => unreachable!("already checked that CONFIG_DIRS is non-empty"); } Ok(()) } एक बार जब get_configuration_directories सफल परिणाम वापस करता है, तो हमें गारंटी मिलती है कि वेक्टर खाली नहीं है। और फिर भी, यदि हम इस वेक्टर के पहले तत्व को प्राप्त करना चाहते हैं, तो हमें first मेथड का उपयोग करना होगा, जो Option<&T> वापस करता है। इसलिए, हमें फिर से एक संभावित रूप से खाली मामले (जहाँ ऑप्शन None है) को हैंडल करने के लिए मजबूर किया जाता है। जैसा कि मूल लेख में कहा गया है, इससे कोड की स्पष्टता, संभावित प्रदर्शन प्रभावों और एक टिकिंग टाइम बॉम्ब में कई समस्याएँ होती हैं, यदि कभी भी get_configuration_directories में इन्वेरिएंट को बदला जाता है। मूल समस्या यह है कि Vec मूल रूप से एक ऐसा टाइप है जो खाली हो सकता है; हम सभी संबंधित कोड पर एक "यह खाली नहीं हो सकता, पिंकी वादा!" टिप्पणी लिख सकते हैं, लेकिन यह किसी भी चीज़ द्वारा औपचारिक रूप से सत्यापित नहीं है। एक "गैर-खाली" वेक्टर के लिए एक टाइप एक "गैर-खाली" वेक्टर के लिए एक टाइप का समाधान है जो एक नए स्थापित इन्वेरिएंट को लागू करने के लिए टाइप सिस्टम का लाभ उठाता है। हम एक अलग टाइप का उपयोग कर सकते हैं जो "एक वेक्टर जो खाली नहीं हो सकता" का प्रतिनिधित्व करता है; वास्तव में, ऐसे टाइप पहले से ही कई रस्ट क्रेट्स में मौजूद हैं - उदाहरण के लिए nonempty: pub struct Non Empty<T> { pub head: T, pub tail: Vec<T>, } इस टाइप का कोई कंस्ट्रक्टर नहीं है जो "कोई तत्व नहीं" की अनुमति देता हो; इसका new एक तत्व लेता है, और इसका first मेथड Option के बिना &T वापस करता है: pub const fn new(e: T) -> Self { Self::singleton(e) } pub const fn singleton(head: T) -> Self { Non Empty { head, tail: Vec::new() } } pub const fn first(&self) -> &T { &self.head } क्रेट का बाकी हिस्सा Non Empty को एक सामान्य Vec के जितना संभव हो उतना करीब से व्यवहार करने के लिए बनाता है, कई उपयोगी ट्रेट्स को लागू करके, साथ ही साथ निम्नलिखित कनवर्जन्स भी: pub fn from_vec(mut vec: Vec<T>) -> Option<Non Empty<T>> { if vec.is_empty() { None } else { let head = vec.remove(0); Some(Non Empty { head, tail: vec }) } } आइए देखें कि यदि हमारा get_configuration_directories फंक्शन एक सामान्य Vec के बजाय एक Non Empty वापस करता है तो यह कैसा दिखेगा: fn get_configuration_directories() -> Result<Non Empty<Path Buf>> { let value = env::var("CONFIG_DIRS").context("could not read CONFIG_DIRS")?; let directories = value.split(',').map(str::trim).map(Path Buf::from).collect(); let Some(directories) = Non Empty::from_vec(directories) else { bail!("CONFIG_DIRS cannot be empty"); }; Ok(directories) } यहाँ Non Empty::from_vec के उपयोग पर ध्यान दें - यह वह जगह है जहाँ इन्वेरिएंट स्थापित होता है। अब...

FAQ प्रश्न: रस्ट में गैर-खाली वेक्टर प्रकार का उपयोग करने का मुख्य लाभ क्या है?

FAQ उत्तर: मुख्य लाभ यह है कि यह टाइप स्तर पर गैर-खाली इन्वेरिएंट को लागू करता है, जिससे रनटाइम चेक और ऑप्शन हैंडलिंग की आवश्यकता समाप्त हो जाती है जब पहले तत्व तक पहुँचा जाता है, जिससे कोड की स्पष्टता और सुरक्षा में सुधार होता है।