रस्ट आपको जीवनकाल क्यों निर्दिष्ट करने के लिए मजबूर करता है? और यह जीवन को कैसे सरल बनाता है?
\n\nयदि आप रस्ट सीख रहे हैं, तो देर-सबेर आप इस रहस्यमय सिंटैक्स का सामना करते हैं: 'a, 'b... और एक स्वाभाविक प्रश्न उठता है: "मुझे स्पष्ट रूप से जीवनकाल क्यों निर्दिष्ट करना चाहिए? अन्य भाषाएँ किसी तरह काम चला लेती हैं!"
यह प्रश्न Stack Overflow पर सबसे लोकप्रिय प्रश्नों में से एक है (हजारों व्यूज)। और यह आश्चर्य की बात नहीं है: रस्ट में lifetimes की अवधारणा तब तक जटिल लगती है जब तक आप इसका सार नहीं समझ लेते। आइए बिना उबाऊ सिद्धांत के, वास्तविक उदाहरणों के साथ समझते हैं।
\n\nसंक्षिप्त उत्तर: गार्बेज कलेक्टर के बिना सुरक्षा
\n\nरस्ट कंपाइलेशन के समय मेमोरी सुरक्षा की गारंटी देता है। उसे यह जानने की आवश्यकता है कि संदर्भ कितने समय तक जीवित रहते हैं, ताकि डैंगलिंग रेफरेंस (dangling references) को रोका जा सके। Java या Go में, गार्बेज कलेक्टर इसका ध्यान रखता है। C++ में, आप स्वयं मेमोरी के लिए जिम्मेदार हैं (और अक्सर गलतियाँ करते हैं)। रस्ट ने तीसरा रास्ता चुना: कंपाइलर स्थिर रूप से सब कुछ जाँचता है।
\n\nलेकिन कंपाइलर स्वयं अनुमान क्यों नहीं लगा सकता? लगा सकता है - 80% मामलों में। इसे lifetime elision (अंतर्निहित अनुमान के नियम) कहा जाता है। हालाँकि, जटिल स्थितियों में, उसे आपकी सहायता की आवश्यकता होती है।
\n\nनियम #1: कंपाइलर मानसिक रोगी नहीं है
\n\nइस फ़ंक्शन को देखें:
\n\nfn longest(x: &str, y: &str) -> &str {\n if x.len() > y.len() { x } else { y }\n}\n\nपहली नज़र में, सब कुछ तार्किक है। लेकिन रस्ट एक त्रुटि देगा: "missing lifetime specifier"। क्यों?
\n\nकल्पना करें कि आपने x पास किया - एक स्ट्रिंग का संदर्भ जो 5 सेकंड तक जीवित रहता है, और y - एक स्ट्रिंग का संदर्भ जो 1 सेकंड तक जीवित रहता है। फ़ंक्शन उनमें से कोई भी लौटा सकता है। लौटाए गए संदर्भ का जीवनकाल क्या होगा? कंपाइलर नहीं जानता!
समाधान - स्पष्ट रूप से कहना: "लौटाया गया संदर्भ दोनों इनपुट संदर्भों के बराबर जीवित रहेगा":
\n\nfn longest<'a>(x: &'a str, y: &'a str) -> &'a str {\n if x.len() > y.len() { x } else { y }\n}\n\nप्रतीक 'a एक जीवनकाल एनोटेशन है। हम कह रहे हैं: "तीनों संदर्भों का न्यूनतम जीवनकाल समान होना चाहिए।" अब कंपाइलर जाँच सकता है कि कॉल करने वाला कोड डेटा को समय से पहले नष्ट नहीं करता है।
नियम #2: अलग-अलग संदर्भ - अलग-अलग जीवन
\n\nशुरुआती लोगों की एक सामान्य गलती यह सोचना है कि सभी 'a समान होने चाहिए। वास्तव में, आप अलग-अलग जीवनकाल निर्दिष्ट कर सकते हैं:
fn example<'a, 'b>(x: &'a str, y: &'b str) -> &'a str {\n x // ठीक है, हम 'a जीवनकाल वाला संदर्भ लौटा रहे हैं\n // y // त्रुटि! y का जीवनकाल 'b है, लेकिन हमने 'a का वादा किया था\n}\n\nयहाँ हम स्पष्ट रूप से कह रहे हैं: x और y का जीवनकाल अलग-अलग हो सकता है। लेकिन हम केवल वही संदर्भ लौटा सकते हैं जिसका जीवनकाल लौटाए गए प्रकार में वादा किए गए जीवनकाल से मेल खाता हो।
नियम #3: स्ट्रक्चर भी संदर्भ संग्रहीत कर सकते हैं
\n\nजब आप एक स्ट्रक्चर बनाते हैं जिसमें संदर्भ होते हैं, तो रस्ट में जीवनकाल को अनिवार्य रूप से निर्दिष्ट करना होता है:
\n\nstruct Book<'a> {\n title: &'a str,\n author: &'a str,\n}\n\nfn main() {\n let title = String::from("Rust Book");\n let book;\n {\n let author = String::from("John Doe");\n book = Book { title: &title, author: &author };\n } // author यहाँ नष्ट हो जाता है!\n println!("{}", book.title); // त्रुटि! book.author लटक रहा है\n}\n\nकंपाइलर इस कोड को कंपाइल नहीं होने देगा, क्योंकि book.author पहले से हटाई गई स्ट्रिंग को संदर्भित करता है। रस्ट में स्पष्ट lifetimes रनटाइम के बजाय कंपाइलेशन के समय ऐसी त्रुटियों को पकड़ने की अनुमति देते हैं।
नियम #4: Lifetime Elision - जब आपको लिखने की आवश्यकता नहीं है
\n\nरस्ट सरल मामलों में स्वयं जीवनकाल का अनुमान लगाने के लिए पर्याप्त स्मार्ट है। यहाँ बताया गया है कि आप कब निर्दिष्ट नहीं कर सकते हैं:
\n\n- \n
- एक इनपुट संदर्भ - आउटपुट का जीवनकाल इनपुट के जीवनकाल के बराबर होता है \n