2015-09-16 8 views
5

पर आवंटित कुछ लौटाएं मेरे पास निम्न सरलीकृत कोड है, जहां एक संरचना A में एक निश्चित विशेषता है। मैं उस विशेषता के मौजूदा संस्करण से A के नए उदाहरण बनाना चाहता हूं, लेकिन मैं फ़ंक्शन कॉल के आखिर में विशेषता के नए मान का जीवनकाल कैसे बना सकता हूं?स्टैक

pub struct A<'a> { 
    some_attr: &'a str, 
} 

impl<'a> A<'a> { 
    fn combine(orig: &'a str) -> A<'a> { 
     let attr = &*(orig.to_string() + "suffix"); 
     A { some_attr: attr } 
    } 
} 

fn main() { 
    println!("{}", A::combine("blah").some_attr); 
} 

उपरोक्त कोड का उत्पादन

error[E0597]: borrowed value does not live long enough 
--> src/main.rs:7:22 
    | 
7 |   let attr = &*(orig.to_string() + "suffix"); 
    |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ does not live long enough 
8 |   A { some_attr: attr } 
9 |  } 
    |  - temporary value only lives until here 
    | 
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 5:1... 
--> src/main.rs:5:1 
    | 
5 |/impl<'a> A<'a> { 
6 | |  fn combine(orig: &'a str) -> A<'a> { 
7 | |   let attr = &*(orig.to_string() + "suffix"); 
8 | |   A { some_attr: attr } 
9 | |  } 
10| | } 
    | |_^ 

उत्तर

12

यह सवाल सबसे निश्चित रूप से पहले उत्तर दिया गया है, लेकिन मैं एक नकली के रूप में यह बंद हो रहा नहीं कर रहा हूँ क्योंकि कोड यहाँ कुछ अलग है और मुझे लगता है कि यह महत्वपूर्ण है।

नोट कैसे आप अपने समारोह में परिभाषित किया गया:

fn combine(orig: &'a str) -> A<'a> 

इसमें कहा गया है कि यह जिसका अंदर प्रदान की स्ट्रिंग के रूप में बिल्कुल के रूप में लंबे समय तक जीवित प्रकार A का एक परिणाम प्रदान करेंगे। हालांकि, समारोह के शरीर इस घोषणा का उल्लंघन करती है:

let attr = &*(orig.to_string() + "suffix"); 
A { 
    some_attr: attr 
} 

यहाँ आप एक नईStringorig से प्राप्त निर्माण, यह का एक टुकड़ा ले और A अंदर वापस जाने के लिए प्रयास करें। हालांकि, orig.to_string() + "suffix" के लिए बनाए गए निहित चर का जीवन इनपुट पैरामीटर के जीवनकाल से सख्ती से छोटा है। इसलिए, आपका प्रोग्राम अस्वीकार कर दिया गया है।

इस पर देखने के लिए एक और अधिक व्यावहारिक तरीका यह मानता है कि to_string() द्वारा बनाई गई स्ट्रिंग और समन्वय को कहीं भी रहना है। हालांकि, आप केवल इसके उधारित टुकड़े को वापस कर देते हैं। इस प्रकार जब कार्य निकलता है, तो स्ट्रिंग नष्ट हो जाती है, और लौटा हुआ टुकड़ा अमान्य हो जाता है। यह वही स्थिति है जो जंग रोकती है।

इस आप कर सकते हैं पर काबू पाने के लिए या तो संग्रहीत करने के लिए एक StringA अंदर:

pub struct A { 
    some_attr: String 
} 

या आप या तो एक टुकड़ा या एक स्वामित्व स्ट्रिंग स्टोर करने के लिए std::borrow::Cow उपयोग कर सकते हैं:

pub struct A<'a> { 
    some_attr: Cow<'a, str> 
} 

पिछले मामले में अपने फ़ंक्शन इस तरह दिख सकता है:

fn combine(orig: &str) -> A<'static> { 
    let attr = orig.to_owned() + "suffix"; 
    A { 
     some_attr: attr.into() 
    } 
} 

ध्यान दें कि क्योंकि आप फ़ंक्शन के अंदर स्ट्रिंग का निर्माण करते हैं, इसे Cow के स्वामित्व वाले रूप के रूप में दर्शाया जाता है और इसलिए आप परिणामस्वरूप मान के लिए 'static आजीवन पैरामीटर का उपयोग कर सकते हैं। इसे orig पर लेना भी संभव है लेकिन ऐसा करने का कोई कारण नहीं है।

Cow के साथ यह भी सीधे स्लाइस से बाहर आवंटन बिना A के मूल्यों को बनाने के लिए संभव है:

fn new(orig: &str) -> A { 
    A { some_attr: orig.into() } 
} 

यहाँ A के जीवनकाल पैरामीटर इनपुट स्ट्रिंग के जीवनकाल के लिए (जीवन इलिजन के माध्यम से) जोड़ दी जाएंगी टुकड़ा। इस मामले में Cow के उधारित संस्करण का उपयोग किया जाता है, और कोई आवंटन नहीं किया जाता है।

भी ध्यान रखें कि यह to_owned() या into()String रों को स्ट्रिंग स्लाइस कन्वर्ट करने के लिए है क्योंकि इन तरीकों को चलाने के लिए स्वरूपण कोड की आवश्यकता नहीं है और इसलिए वे अधिक कुशल हैं उपयोग करने के लिए बेहतर है।

आप इसे जीवन भर पर कैसे लौट सकते हैं जब आप इसे फ्लाई पर बना रहे हैं? सुनिश्चित नहीं है कि "Cow" का स्वामित्व वाला संस्करण क्या है और क्यों यह 'static संभव बनाता है।

pub enum Cow<'a, B> where B: 'a + ToOwned + ?Sized { 
    Borrowed(&'a B), 
    Owned(B::Owned), 
} 

यह जटिल लग रहा है, लेकिन यह तथ्य सरल है:

यहाँ Cow की परिभाषा है। Cow का एक उदाहरण या तो B या किसी स्वामित्व वाले मान का संदर्भ हो सकता है जिसे B से ToOwned विशेषता के माध्यम से लिया जा सकता है। क्योंकि str लागू करता ToOwned जहां Owned जुड़े प्रकार String (ToOwned<Owned = String>, जब यह enum str के लिए विशेष है के रूप में लिखा करने के लिए बराबर होती है, यह इस तरह दिखता है:

pub enum Cow<'a, str> { 
    Borrowed(&'a str), 
    Owned(String) 
} 
इसलिए

, Cow<str> या तो एक स्ट्रिंग टुकड़ा या एक स्वामित्व स्ट्रिंग का प्रतिनिधित्व कर सकते हैं - और जब तक Cow वास्तव में क्लोन-ऑन-राइट कार्यक्षमता के लिए तरीके प्रदान करता है, यह बस के रूप में अक्सर एक मूल्य है जो या तो उधार लिया जा सकता या आदेश अतिरिक्त आवंटन से बचने के लिए स्वामित्व धारण करने के लिए प्रयोग किया जाता है। क्योंकि Cow<'a, B> लागू करता Deref<Target = B>, आप Cow<'a, B> से &B प्राप्त कर सकते हैं सरल रेबो के साथ रोइंग: xCow<str> है, तो &*x&str है, चाहे x के अंदर क्या है, स्वाभाविक रूप से, आप Cow के दोनों प्रकारों से एक टुकड़ा निकाल सकते हैं।

आप देख सकते हैं कि Cow::Owned संस्करण में इसके अंदर कोई संदर्भ नहीं है, केवल String। इसलिए, Cow का मान Owned संस्करण का उपयोग करके बनाया गया है, आप किसी भी जीवनकाल को चुन सकते हैं (याद रखें, आजीवन पैरामीटर जेनेरिक टाइप पैरामीटर की तरह हैं; विशेष रूप से, यह कॉलर है जो उन्हें चुनने के लिए मिलता है) - वहां हैं इस पर कोई प्रतिबंध नहीं है। तो यह सबसे अच्छा जीवनकाल संभव के रूप में 'static चुनने के लिए समझ में आता है।

क्या orig.to_owned इस समारोह को कॉल करने वाले व्यक्ति से स्वामित्व हटा दें? ऐसा लगता है कि यह असुविधाजनक होगा।

to_owned() विधि ToOwned विशेषता के अंतर्गत आता है:

pub trait ToOwned { 
    type Owned: Borrow<Self>; 
    fn to_owned(&self) -> Self::Owned; 
} 

इस विशेषता OwnedString के बराबर के साथ str द्वारा कार्यान्वित किया जाता। to_owned() विधि किसी भी मूल्य के स्वामित्व वाले संस्करण को लौटाती है जिसे इसे कहा जाता है। इस विशेष मामले में, यह String&str से बाहर बनाता है, प्रभावी ढंग से स्ट्रिंग स्लाइस की सामग्री को नए आवंटन में कॉपी करता है। इसलिए नहीं, to_owned() स्वामित्व हस्तांतरण का संकेत नहीं देता है, ऐसा लगता है कि यह एक "स्मार्ट" क्लोन का तात्पर्य है।

जहां तक ​​मेरा बता सकते हैं स्ट्रिंग Into<Vec<u8>> नहीं बल्कि str लागू करता है, तो कैसे हम 2 उदाहरण में into() कॉल कर सकते हैं?

Into विशेषता बहुत बहुमुखी है और इसे मानक पुस्तकालय में कई प्रकार के लिए लागू किया गया है। Into आमतौर पर From विशेषता के माध्यम से लागू किया जाता है: यदि T: From<U>, तो U: Into<T>। वहाँ मानक पुस्तकालय में From के दो महत्वपूर्ण कार्यान्वयन हैं:

impl<'a> From<&'a str> for Cow<'a, str> 

impl<'a> From<String> for Cow<'a, str> 

ये कार्यान्वयन बहुत सरल हैं - वे केवल वापसी Cow::Borrowed(value) अगर value&str और Cow::Owned(value) है अगर valueString है।

इसका मतलब है कि &'a str और StringInto<Cow<'a, str>> लागू, और इसलिए वे into() विधि के साथ Cow में बदला जा सकता। मेरे उदाहरण में यह बिल्कुल होता है - मैं into() का उपयोग कर String या &str से Cow<str> को परिवर्तित करने के लिए उपयोग कर रहा हूं। इस स्पष्ट रूपांतरण के बिना आपको बेमेल प्रकारों के बारे में एक त्रुटि मिलेगी।

+0

धन्यवाद, यह काम करता है! लेकिन यहां बहुत कुछ है जो मुझे समझ में नहीं आता है, जब आप इसे फ्लाई पर बना रहे हैं तो आप जीवनकाल 'स्थिर' के 'ए' को कैसे वापस कर सकते हैं? सुनिश्चित नहीं है कि "गाय' के स्वामित्व वाले संस्करण" का अर्थ क्या है और यह क्यों 'स्थैतिक' संभव बनाता है। हम किसी भी बिंदु पर 'some_attr' को कभी भी संशोधित नहीं कर रहे हैं - क्या आमतौर पर लिखने की अनुमति देने के लिए "लिखने पर प्रतिलिपि" का बिंदु नहीं है? क्या 'orig.to_owned' इस फ़ंक्शन को कॉल करने वाले व्यक्ति से स्वामित्व हटा देता है? ऐसा लगता है कि यह असुविधाजनक होगा। जहां तक ​​मैं 'स्ट्रिंग' लागू कर सकता हूं '>' लेकिन 'str' में नहीं, तो हम दूसरे उदाहरण में '()' को कैसे कॉल कर सकते हैं? – wrongusername

+0

वाह, यह बहुत सारे प्रश्न हैं! मैंने अपना जवाब अपडेट कर लिया है, उम्मीद है कि यह सहायक होगा :) –