क्यों उच्च स्तर की विशेषता सीमा में 'st :: :: mem :: drop` बिल्कुल बंद होने के समान नहीं है? _ ()?


13

का कार्यान्वयन std::mem::dropनिम्नलिखित होने के लिए प्रलेखित है:

pub fn drop<T>(_x: T) { }

जैसे, मैं दोनों दिशाओं में संभावित 1: 1 प्रतिस्थापन के लिए क्लोजर |_| ()( टॉयलेट क्लोजर के रूप में जाना जाता है ) को बंद करने की उम्मीद करूंगा drop। हालांकि, नीचे दिए गए कोड से पता चलता है कि dropफ़ंक्शन के पैरामीटर पर बाध्य उच्च रैंक वाली विशेषता के साथ संगत नहीं है, जबकि शौचालय बंद है।

fn foo<F, T>(f: F, x: T)
where
    for<'a> F: FnOnce(&'a T),
{
    dbg!(f(&x));
}

fn main() {
    foo(|_| (), "toilet closure"); // this compiles
    foo(drop, "drop"); // this does not!
}

संकलक का त्रुटि संदेश:

error[E0631]: type mismatch in function arguments
  --> src/main.rs:10:5
   |
1  | fn foo<F, T>(f: F, x: T)
   |    ---
2  | where
3  |     for<'a> F: FnOnce(&'a T),
   |                ------------- required by this bound in `foo`
...
10 |     foo(drop, "drop"); // this does not!
   |     ^^^
   |     |
   |     expected signature of `for<'a> fn(&'a _) -> _`
   |     found signature of `fn(_) -> _`

error[E0271]: type mismatch resolving `for<'a> <fn(_) {std::mem::drop::<_>} as std::ops::FnOnce<(&'a _,)>>::Output == ()`
  --> src/main.rs:10:5
   |
1  | fn foo<F, T>(f: F, x: T)
   |    ---
2  | where
3  |     for<'a> F: FnOnce(&'a T),
   |                ------------- required by this bound in `foo`
...
10 |     foo(drop, "drop"); // this does not!
   |     ^^^ expected bound lifetime parameter 'a, found concrete lifetime

यह देखते हुए कि dropकिसी भी आकार के संबंध में माना जाता है कि सामान्य है T, यह अनुचित लगता है कि "अधिक सामान्य" हस्ताक्षर fn(_) -> _साथ संगत नहीं है for<'a> fn (&'a _) -> _। कम्पाइलर dropयहाँ के हस्ताक्षर को स्वीकार क्यों नहीं कर रहा है, और जब टॉयलेट के बंद होने के बाद उसे रखा जाता है, तो क्या होता है?

जवाबों:


4

इस मुद्दे का मूल यह है कि dropकोई एकल कार्य नहीं है, बल्कि कार्यों का एक पैरामीटर सेट है जो प्रत्येक विशेष प्रकार को छोड़ता है। एक उच्च श्रेणी की अनुदित सीमा (इसके बाद hrtb) को संतुष्ट करने के लिए, आपको एक ऐसे फ़ंक्शन की आवश्यकता होगी जो एक साथ किसी भी जीवनकाल के साथ एक प्रकार का संदर्भ ले सके।


हम dropसामान्य कार्य के हमारे विशिष्ट उदाहरण के रूप में उपयोग करेंगे, लेकिन यह सब आम तौर पर भी लागू होता है। यहाँ संदर्भ के लिए कोड है: fn drop<T>(_: T) {}

वैचारिक रूप से, dropएक एकल फ़ंक्शन नहीं है, बल्कि हर संभव प्रकार के लिए एक फ़ंक्शन है T। किसी dropभी प्रकार का कोई विशेष उदाहरण केवल एक प्रकार का तर्क देता है। इसे मोनोमोर्फाइजेशन कहा जाता है । यदि एक अलग के Tसाथ प्रयोग किया जाता है drop, का एक अलग संस्करण dropसंकलित किया जाता है। इसीलिए आप एक जेनेरिक फ़ंक्शन को एक तर्क के रूप में पारित नहीं कर सकते हैं और उस फ़ंक्शन को पूर्ण सामान्यता में उपयोग कर सकते हैं ( यह प्रश्न देखें )

दूसरी ओर, एक फंक्शन जैसे fn pass(x: &i32) -> &i32 {x}hrtb को संतुष्ट करता है for<'a> Fn(&'a i32) -> &'a i32। इसके विपरीत drop, हमारे पास एक ही कार्य है जो एक साथ प्रत्येक जीवनकाल के Fn(&'a i32) -> &'a i32लिए संतुष्ट करता है । यह परिलक्षित होता है कि इसका उपयोग कैसे किया जा सकता है।'apass

fn pass(x: &i32) -> &i32 {
    x
}

fn two_uses<F>(f: F)
where
    for<'a> F: Fn(&'a i32) -> &'a i32, // By the way, this can simply be written
                                       // F: Fn(&i32) -> &i32 due to lifetime elision rules.
                                       // That applies to your original example too.
{
    {
        // x has some lifetime 'a
        let x = &22;
        println!("{}", f(x));
        // 'a ends around here
    }
    {
        // y has some lifetime 'b
        let y = &23;
        println!("{}", f(y));
        // 'b ends around here
    }
    // 'a and 'b are unrelated since they have no overlap
}

fn main() {
    two_uses(pass);
}

(खेल का मैदान)

उदाहरण में, जीवनकाल 'aऔर 'bजिनका एक दूसरे से कोई संबंध नहीं है: न तो पूरी तरह से एक दूसरे को शामिल करता है। इसलिए यहां किसी तरह की सबटाइपिंग चीज नहीं है। passवास्तव में दो अलग, असंबंधित जीवनकाल के साथ एक एकल उदाहरण का उपयोग किया जा रहा है।

यही कारण dropहै कि संतुष्ट नहीं है for<'a> FnOnce(&'a T)। किसी भी विशेष उदाहरण को dropकेवल एक जीवनकाल (उपगण की अनदेखी) कवर कर सकता है । अगर हम पारित कर दिया dropमें two_uses(मामूली हस्ताक्षर परिवर्तन और यह सोचते हैं संकलक हमें के साथ) ऊपर के उदाहरण से, यह कुछ विशेष जीवन का चयन करने के लिए होता है 'aऔर के कहने dropके दायरे में two_usesहोगा Fn(&'a i32)कुछ के लिए ठोस जीवन भर 'a। चूंकि फ़ंक्शन केवल एकल जीवनकाल पर लागू होगा 'a, इसलिए इसे दो असंबंधित जीवनकाल के साथ उपयोग करना संभव नहीं होगा।

तो शौचालय को बंद करने से एचआरटीबी क्यों मिलता है? एक बंद करने के लिए प्रकार का उल्लेख करते समय, यदि अपेक्षित प्रकार संकेत देता है कि एक उच्च-रैंक वाली विशेषता बाध्य है, तो संकलक एक फिट बनाने की कोशिश करेगा । इस मामले में, यह सफल होता है।


अंक # 41078 इससे निकटता से जुड़ा हुआ है और विशेष रूप से, यहाँ eddyb की टिप्पणी अनिवार्य रूप से उपरोक्त स्पष्टीकरण देती है (हालांकि सामान्य कार्यों के बजाय क्लोजर के संदर्भ में)। समस्या स्वयं वर्तमान समस्या का समाधान नहीं करती है। इसके बजाय यह पता चलता है कि यदि आप शौचालय को बंद करने से पहले एक चर का उपयोग करते हैं तो इसे क्या कहते हैं (इसे आज़माएं!)।

यह संभव है कि भविष्य में स्थिति बदल जाएगी, लेकिन यह एक बहुत बड़े बदलाव की आवश्यकता होगी कि सामान्य कार्यों को कैसे मोनोमोर्फ किया जाता है।


4

संक्षेप में, दोनों पंक्तियों को विफल होना चाहिए। लेकिन hrtb जीवनकाल को संभालने के पुराने तरीके से एक कदम के बाद से, अर्थात् रिसाव की जाँच , वर्तमान में कुछ ध्वनि मुद्दा है, rustcसमाप्त होता है (गलत तरीके से) एक को स्वीकार करने और एक बहुत बुरा त्रुटि संदेश के साथ छोड़ रहा है।

यदि आप लीक चेक को निष्क्रिय कर देते हैं rustc +nightly -Zno-leak-check, तो आप एक अधिक समझदार त्रुटि संदेश देख पाएंगे:

error[E0308]: mismatched types
  --> src/main.rs:10:5
   |
10 |     foo(drop, "drop");
   |     ^^^ one type is more general than the other
   |
   = note: expected type `std::ops::FnOnce<(&'a &str,)>`
              found type `std::ops::FnOnce<(&&str,)>`

इस त्रुटि के बारे में मेरी व्याख्या यह है कि फ़ंक्शन &xके शरीर में fooकेवल एक स्कोप आजीवन उक्त बॉडी तक ही सीमित होता है, इसलिए f(&x)इसमें एक ही स्कोप लाइफटाइम भी होता है, जो संभवत: for<'a>ट्रिट बाउंड द्वारा आवश्यक यूनिवर्सल क्वांटिफिकेशन को संतुष्ट नहीं कर सकता है ।

आपके द्वारा यहां प्रस्तुत प्रश्न # 57642 जारी करने के लिए लगभग समान है , जिसमें दो विपरीत भाग भी हैं।

Hrtb जीवनकाल को संसाधित करने का नया तरीका तथाकथित ब्रह्मांडों का उपयोग करके है । निको के पास सार्वभौमिक लोगों के साथ रिसाव की जांच करने के लिए एक WIP है। इस नए शासन के तहत, ऊपर दिए गए मुद्दे # 57642 के दोनों भागों को सभी अधिक स्पष्ट निदानों के साथ विफल बताया गया है । मुझे लगता है कि कंपाइलर तब तक भी आपके उदाहरण कोड को सही ढंग से संभालने में सक्षम होना चाहिए।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.