प्रॉम्प्ट और दायरा
आप Rust 1.85 की एक ऐसी सर्विस का रखरखाव करते हैं जो एक एसिंक्रोनस रीट्रीयर (retryer) को कॉलबैक पास करती है। एक कॉलबैक को कॉलर के स्वामित्व वाले बफर को बॉरो करना होगा, एसिंक्रोनस I/O निष्पादित करना होगा, और विभिन्न बॉरो लाइफटाइम्स के साथ काम करना होगा। async || {} की तुलना || async {} से करें, फिर AsyncFn, लाइफटाइम्स, कैप्चर, कैंसिलेशन और माइग्रेशन को कवर करें।
साक्षात्कारकर्ता क्या टेस्ट कर रहा है
- क्या आप समझते हैं कि एक async-क्लोजर फ़्यूचर कैप्चर किए गए डेटा को बॉरो कर सकता है।
- क्या आप हायर-ऑर्डर async कॉलबैक के लिए
AsyncFn,AsyncFnMut, औरAsyncFnOnceका उपयोग करते हैं। - क्या आप फ़्यूचर बनने, पोल होने, या रद्द होने पर ओनरशिप और लाइफटाइम्स के बीच अंतर करते हैं।
- क्या आप वर्ज़न माइग्रेशन, बाउंड्री टेस्ट और रिसोर्स-क्लीनअप योजना प्रदान करते हैं।
पूछने के लिए स्पष्टीकरण संबंधी प्रश्न
- क्या कॉलबैक को बार-बार कॉल किया जाता है, यह कैप्चर की गई स्थिति को म्यूटेट करता है, या इसे एक बार इस्तेमाल (consume) किया जाता है? यह ट्रेट का चयन करता है।
- क्या इनपुट ओन्ड (owned) है या एक शॉर्ट बॉरो है? क्या फ़्यूचर को कॉल के दौरान पूरा होना चाहिए?
- क्या कैंसिलेशन I/O के दौरान होता है या जब रीट्रीयर बाहर निकलता है? बाहरी संसाधनों को कैसे साफ किया जाता है?
- क्या MSRV Rust 1.85 पर ले जाया गया है, और क्या डिपेंडेंसीज़ Rust 2024 का समर्थन करती हैं?
30-सेकंड का उत्तर ढांचा
|| async {} एक सामान्य क्लोजर है जो एक async ब्लॉक लौटाता है; इसका आंतरिक फ़्यूचर async क्लोजर की तरह समान बॉरोइंग संबंध व्यक्त नहीं कर सकता है। Rust 1.85 async || {} को फर्स्ट-क्लास async कॉल बनाता है और AsyncFn ट्रेट्स जोड़ता है। कॉल पैटर्न के आधार पर ट्रेट चुनें, बताएं कि एक इनपुट बॉरो तब तक रहता है जब तक फ़्यूचर पूरा नहीं हो जाता, और फ़्यूचर ड्रॉप को कैंसिलेशन क्लीनअप करने दें। पहले टूलचेन और एडिशन को माइग्रेट करें, cargo fix का सावधानीपूर्वक उपयोग करें, फिर कंपाइलर और रनटाइम टेस्ट के साथ बॉरोइंग, रीट्रीज़ और कैंसिलेशन को सत्यापित करें।
चरण-दर-चरण गहन विश्लेषण
1. दोनों रूपों की तुलना करें
एक सामान्य क्लोजर एक फ़्यूचर लौटाता है:
let old = |buf: &mut Vec<u8>| async move { buf.push(1); };एक async क्लोजर एसिंक्रोनस कॉल को ही क्लोजर कॉन्ट्रैक्ट का हिस्सा बनाता है:
let new = async |buf: &mut Vec<u8>| { buf.push(1); };मुख्य प्रश्न यह है कि क्या प्रत्येक कॉल उस कॉल के इनपुट से जुड़ा हुआ फ़्यूचर उत्पन्न कर सकती है। पहला रूप अक्सर हायर-ऑर्डर लाइफटाइम बाधाओं को व्यक्त करना कठिन बना देता है; दूसरा रूप AsyncFn ट्रेट्स द्वारा दर्शाया जाता है।
2. एक AsyncFn ट्रेट चुनें
रीड-ओनली कॉलबैक के लिए AsyncFn का उपयोग करें जिसे बार-बार कॉल किया जा सकता है, जब बार-बार कॉल कैप्चर की गई स्थिति को म्यूटेट करती हैं तो AsyncFnMut का उपयोग करें, और जब कॉलबैक अपने कैप्चर को कन्ज़्यूम करता है तो AsyncFnOnce का उपयोग करें। केवल एसिंक्रोनस होने के कारण प्रत्येक फ़्यूचर को BoxFuture में बाध्य न करें; यह वैध शॉर्ट बॉरो को अस्वीकार कर देगा।
3. लाइफटाइम्स और कैप्चर
एक इनपुट बॉरो को तब तक जीवित रहना चाहिए जब तक कि फ़्यूचर पूरा न हो जाए या ड्रॉप न हो जाए। कॉलबैक को उस बॉरो को 'static टास्क में नहीं रखना चाहिए, और रीट्रीयर को बफर के लाइफटाइम से परे फ़्यूचर को कतार में नहीं लगाना चाहिए। वास्तविक बैकग्राउंड कार्य के लिए, पहले ओन्ड डेटा को कॉपी या मूव करें ताकि टास्क अपने लाइफटाइम का मालिक बन सके।
4. कैंसिलेशन और क्लीनअप
Rust में कोई अनिवार्य async-कैंसिलेशन प्रोटोकॉल नहीं है; फ़्यूचर ड्रॉप आमतौर पर कैंसिलेशन का प्रतिनिधित्व करता है। I/O रैपर्स को ड्रॉप पर या एक स्पष्ट कैंसिलेशन टोकन के माध्यम से सॉकेट्स को बंद करना चाहिए, लॉक्स को रिलीज़ करना चाहिए और अस्थायी फ़ाइलों को हटाना चाहिए। एक रीट्रीयर को एक ही फ़्यूचर को समवर्ती रूप से पोल नहीं करना चाहिए या कैंसिलेशन के बाद स्थिति का उपयोग नहीं करना चाहिए।
5. रीट्रीज़ और साइड इफेक्ट्स
केवल इडेम्पोटेंट (idempotent) ऑपरेशन्स को ही स्वचालित रूप से पुनः प्रयास करना चाहिए। नॉन-इडेम्पोटेंट I/O के लिए अनुरोध आईडी, लेनदेन, या मुआवज़े (compensation) की आवश्यकता होती है। प्रत्येक प्रयास के लिए एक नया फ़्यूचर बनाएं और प्रयास, त्रुटि और कैंसिलेशन के कारण को रिकॉर्ड करें। यदि कोई बाहरी साइड इफेक्ट कमिट हो गया हो सकता है, तो पुनः प्रयास करने से पहले इसे क्वेरी करें या इडेम्पोटेंसी कुंजी का उपयोग करें।
6. माइग्रेशन और सत्यापन
CI, डेवलपमेंट एनवायरनमेंट्स और MSRV को Rust 1.85 पर ले जाएं, फिर Rust 2024 माइग्रेशन गाइड का पालन करें। cargo fix --edition रूढ़िवादी है और सिमेंटिक समीक्षा का स्थान नहीं ले सकता है। शॉर्ट बॉरो, म्यूटेबल कैप्चर, बार-बार कॉल्स, फ़्यूचर ड्रॉप, टाइमआउट, रीट्रीज़ और प्रत्येक समर्थित टारगेट का परीक्षण करें।
मॉडल उच्च-गुणवत्ता वाला उत्तर
मैं इस आधार पर AsyncFn, AsyncFnMut, या AsyncFnOnce चुनूंगा कि क्या कॉलबैक दोहराता है और कैप्चर को कन्ज़्यूम करता है या म्यूटेट करता है। async || सीधे एक async क्लोजर को व्यक्त करता है, इसलिए प्रत्येक कॉल का फ़्यूचर उसके इनपुट बॉरो से बंधा हो सकता है; हायर-ऑर्डर जेनेरिक्स में || async {} को इस तरह से बाधित करना अधिक कठिन है। मैं शॉर्ट-बॉरो फ़्यूचर को 'static के रूप में संग्रहीत नहीं करूंगा; इसके बजाय एक बैकग्राउंड टास्क को ओन्ड डेटा प्राप्त होता है। फ़्यूचर ड्रॉप या कैंसिलेशन टोकन I/O और लॉक्स को साफ करता है। रीट्रीज़ केवल इडेम्पोटेंट ऑपरेशन्स तक सीमित हैं और एक प्रयास और अनुरोध आईडी रिकॉर्ड करते हैं। Rust 1.85 पर माइग्रेट करने के बाद, कंपाइलर, Miri, और रनटाइम टेस्ट लाइफटाइम्स, रीट्रीज़ और कैंसिलेशन को कवर करते हैं।
सामान्य गलतियाँ
- दोनों रूपों को समान बताना → कैप्चर किए गए बॉरो और हायर-ऑर्डर ट्रेट सिमेंटिक्स को अनदेखा करना → शॉर्ट-बॉरो कॉलबैक का परीक्षण करें।
- प्रत्येक कॉलबैक के लिए
'staticकी आवश्यकता होना → वैध फोरग्राउंड बॉरो को अस्वीकार करना → बॉरो की गई कॉल्स को ओन्ड बैकग्राउंड डेटा से अलग करें। - केवल एक बूलियन कैंसिलेशन फ़्लैग का उपयोग करना → I/O अभी भी लॉक्स या सॉकेट्स रखता है → ड्रॉप और टोकन क्लीनअप पाथ प्रदान करें।
- नॉन-इडेम्पोटेंट कार्य को हमेशा के लिए पुनः प्रयास करना → साइड इफेक्ट्स की नकल करना → अनुरोध आईडी, क्वेरीज़ या मुआवज़े का उपयोग करें।
cargo fixको संपूर्ण माइग्रेशन मानना → सिमेंटिक और MSRV जोखिम छोड़ना → क्रॉस-टारगेट और व्यवहार परीक्षण जोड़ें।
फॉलो-अप प्रश्न और उत्तर
प्रत्येक कॉलबैक फ़्यूचर को 'static के रूप में बॉक्स क्यों न करें?
यह इनपुट बॉरो से जुड़े लाइफटाइम को खो देता है और वैध शॉर्ट-बॉरो कॉलबैक को अस्वीकार कर देता है। केवल लंबे समय तक चलने वाले बैकग्राउंड टास्क में प्रवेश करने वाले डेटा का स्वामित्व पहले होना चाहिए और फिर 'static बनना चाहिए।
यदि एक AsyncFnMut कॉलबैक को समवर्ती रूप से कॉल किया जाता है तो क्या होता है?
यह म्यूटेबल कैप्चर को व्यक्त करता है लेकिन कंकरेंसी सुरक्षा प्रदान नहीं करता है। कॉल्स को सीरियलाइज़ करें, सिंक्रोनाइज़ेशन जोड़ें, या प्रत्येक कॉल को स्वतंत्र स्थिति दें; कभी भी ओवरलैपिंग म्यूटेबल बॉरो न बनाएं।
जब I/O के दौरान फ़्यूचर ड्रॉप हो जाता है तो आप क्लीनअप कैसे साबित करते हैं?
संसाधन को Drop या एक स्पष्ट कैंसिलेशन पाथ के साथ लपेटें, टाइमआउट और टास्क कैंसिलेशन का परीक्षण करें, और अंतिम कनेक्शन, लॉक, अस्थायी फ़ाइल और बाहरी अनुरोध-आईडी स्थिति का निरीक्षण करें।