मैं जंग से शुरू कर रहा हूं, और मैं पहले से ही डेटा स्वामित्व के साथ समस्याओं का सामना कर रहा हूं।

मैं Port<T> नामक एक सामान्य संरचना को कार्यान्वित करना चाहता हूं जिसमें मूल्यों का वेक्टर है Vec<T>। साथ ही, इस संरचना में एक ही प्रकार के अन्य structs के संदर्भ-गिनती पॉइंटर्स का वेक्टर है, Vec<Rc<Port<T>>>:

use std::slice::Iter;
use std::rc::Rc;

pub struct Port<T> {
    values: Vec<T>,
    ports: Vec<Rc<Port<T>>>,
}

विचार निम्नलिखित है: Port<T> प्रकार की कई संरचनाएं हैं। आप किसी दिए गए पोर्ट में T प्रकार का मान जोड़ सकते हैं। प्रत्येक पोर्ट इन मानों को अपनी values विशेषता में संग्रहीत करता है। हालांकि, संदर्भ-गिनती पॉइंटर्स का उपयोग करके एक बंदरगाह को दूसरों के लिए "श्रृंखला" करना संभव है:

impl <T> Port<T> {
    pub fn new() -> Self {
        Self { values: vec![], ports: vec![] }
    }

    pub fn add_value(&mut self, value: T) {
        self.values.push(value);
    }

    pub fn chain_port(&mut self, port: Rc<Port<T>>) {
        if !port.is_empty() {
            self.ports.push(port)
        }
    }

    pub fn is_empty(&self) -> bool {
        self.values.is_empty() || self.ports.is_empty()
    }

    pub fn clear(&mut self) {
        self.values.clear();
        self.ports.clear();
    }
}

अब तक, कोड संकलित करता है। अब, मैं एक बंदरगाह के लिए एक पुनरावर्तक लागू करना चाहता हूं जो इस बंदरगाह के स्वामित्व वाले मूल्यों के संदर्भ देता है लेकिन प्रत्येक जंजीर बंदरगाह के पुनरावर्तक द्वारा उत्पन्न मूल्यों के संदर्भ भी देता है:

pub struct PortIterator<'a, T> {
    values: Iter<'a, T>,                     // Iterates over values owned by Port<T>
    port: Option<Box<PortIterator<'a, T>>>,  // Pointer to current port iterator
    ports: Vec<Rc<Port<T>>>,                 // Pointers to remaining chained ports
}

// Note that the iterator is created from an immutable reference to Port<T>
impl<'a, T: 'a> IntoIterator for &'a Port<T> {
    type Item = &'a T;  // the iterator returns references to values
    type IntoIter = PortIterator<'a, T>;

    fn into_iter(self) -> Self::IntoIter {
        // We clone all the reference-counting pointers so the iterator owns them
        let mut ports = vec![];
        for port in self.ports.iter() {
            ports.push(port.clone())
        }
        PortIterator {values: self.values.iter(), port: None, ports}
    }
}

अब, आइए PortIterator के लिए Iterator विशेषता को परिभाषित करें:

impl <'a, T: 'a> Iterator for PortIterator<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<Self::Item> {
        // We first iterate over values of the original port
        if let Some(v) = self.values.next() {
            return Some(v)
        }
        // If the first iterable is done, we try to iterate over a chained port
        if let Some(port) = &mut self.port {
            if let Some(v) = port.next() {
                return Some(v)
            }
        }
        // if the chained port is over, we try to consume the next chained port
        if let Some(port) = self.ports.get(self.next_port) {
            self.next_port += 1;
            self.port = Some(Box::new(port.as_ref().into_iter()));
            return self.next()
        }
        None
    }
}

अब, कार्यक्रम संकलित नहीं करता है। समस्या तीसरे if let ब्लॉक में लगती है, और इसका संबंध जीवन काल से है। यह वही है जो संकलक कहता है:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
  --> src/modeling/port.rs:69:40
   |
69 |         if let Some(port) = self.ports.get(self.next_port) {
   |                                        ^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 57:5...
  --> src/modeling/port.rs:57:5
   |
57 |     fn next(&mut self) -> Option<Self::Item> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that reference does not outlive borrowed content
  --> src/modeling/port.rs:69:29
   |
69 |         if let Some(port) = self.ports.get(self.next_port) {
   |                             ^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 54:7...
  --> src/modeling/port.rs:54:7
   |
54 | impl <'a, T: 'a> Iterator for PortIterator<'a, T> {
   |       ^^
note: ...so that the expression is assignable
  --> src/modeling/port.rs:71:25
   |
71 |             self.port = Some(Box::new(port.as_ref().into_iter()));
   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: expected `Option<Box<PortIterator<'a, _>>>`
              found `Option<Box<PortIterator<'_, _>>>`

मुझे यकीन नहीं है कि इससे कैसे निपटें। मैं अन्य विकल्पों और कार्यान्वयन की कोशिश कर रहा हूं, लेकिन मैं मंडलियों में जा रहा हूं।

2
Román Cárdenas 7 जून 2021, 14:31

2 जवाब

सबसे बढ़िया उत्तर

मुझे लगता है कि आप जो हासिल करना चाहते हैं उसे हासिल करने के आसान तरीके हैं। आइए छोटी शुरुआत करें: आपके Port<T> को एक iter(&self) विधि की आवश्यकता है जो एक पुनरावर्तक लौटाता है जो &T आइटम सौंपता है:

pub fn iter(&self) -> impl Iterator<Item = &T> {
    // ...
}

इस फ़ंक्शन को इटरेटर को self.values, यानी self.values.iter() पर एक इटरेटर के साथ जंजीर वाले बंदरगाहों पर श्रृंखलाबद्ध करने की आवश्यकता है। आप जो लिखना चाहते हैं वह कुछ इस प्रकार है:

pub fn iter(&self) -> impl Iterator<Item = &T> {
    self.values
        .iter()
        .chain(self.ports.iter().flat_map(|p| p.iter()))
}

हालांकि, यह संकलित नहीं करता है क्योंकि संकलक "पुनरावर्ती अपारदर्शी प्रकार" की शिकायत करता है। ऐसा इसलिए है क्योंकि p.iter() का प्रकार हमारा वही impl Iterator<...> है, जिसे तब खुद को समाहित करना होगा। यह वैचारिक रूप से वही समस्या है जिसका सामना आपने PortIterator के निर्माण के समय किया था, जिसे आपने जंजीर में जकड़े हुए PortIterator को बॉक्स करके हल किया था। हम इसे उसी तरह हल कर सकते हैं, आंतरिक पुनरावर्तक को बॉक्स करके और इसे गतिशील रूप से भेजकर:

pub fn iter(&self) -> impl Iterator<Item = &T> {
    self.values.iter().chain(
        self.ports
            .iter()
            .flat_map(|p| Box::new(p.iter()) as Box<dyn Iterator<Item = &T>>),
    )
}
3
user4815162342 7 जून 2021, 17:20
आपका बहुत बहुत धन्यवाद! हाँ, यह तरीका बहुत बेहतर लगता है। मैं इसका परीक्षण कर रहा हूं और मुझे पता चला है कि, एक बार जब आप एक बंदरगाह को दूसरे से जोड़ते हैं, तो पूर्व को स्थानांतरित कर दिया जाता है और अब इसका उपयोग नहीं किया जा सकता है। मुझे लगता है कि इसे ठीक करने के लिए मुझे Vec<Rc<RefCell<Port<T>>>> का उपयोग करना होगा।
 – 
Román Cárdenas
7 जून 2021, 17:18
मुझे लगता है कि यह अच्छी तरह से काम करेगा, और iter() जैसी विधि होने से जंग के प्रकारों के लिए सम्मेलन होता है, उदा। Vec में एक है।
 – 
user4815162342
7 जून 2021, 17:28
1
&Port<T> के लिए IntoIterator को लागू करने के लिए, कोई flat_map() को दिए गए क्लोजर को एक फ़ंक्शन के साथ बदल सकता है (जो संभव होना चाहिए क्योंकि क्लोजर कुछ भी कैप्चर नहीं करता है) और केवल एक प्रकार का जेनेरिक लौटाता है T Port::iter() से। वापसी का प्रकार Chain<Iter<T, FlatMap<slice::Iter<Rc<Port<T>>>, Box<dyn Iterator<Item = &T>>, fn(&Rc<Port<T>>) -> Box<dyn Iterator<Item = &T>>> जैसा कुछ होगा। हालांकि यह आजीवन मुद्दों के कारण संकलित नहीं हुआ, और वास्तव में वास्तव में जल्दी से बालों वाला हो गया। अगर आपको बिल्कुल IntoIterator की जरूरत नहीं है, तो मैं इसके साथ जाऊंगा।
 – 
user4815162342
7 जून 2021, 17:28
आपके उत्तर के लिए धन्यवाद। मैं बंदरगाहों का पीछा करने के लिए Rc>> का उपयोग करने का प्रयास कर रहा हूं, लेकिन मुझे नए मुद्दों का सामना करना पड़ रहा है। जैसा कि आपने सुझाव दिया, मैंने अपने मामले के बारे में अधिक विवरण के साथ एक नया प्रश्न पोस्ट किया: stackoverflow.com/questions/67899965/…
 – 
Román Cárdenas
9 जून 2021, 11:01

@ user4815162342 के उत्तर (जिसे स्वीकार किया गया है) के लिए धन्यवाद, मैं एक साधारण उदाहरण संकलित करने और चलाने में सक्षम था। हालांकि, एक बार एक Port<T> को एक Rc में ले जाया गया और अन्य Port<T> में जंजीर से बांध दिया गया, तो पहले वाले में डेटा जोड़ना तब तक संभव नहीं था जब तक कि बाद में साफ नहीं हो जाता। इसलिए मैंने इसके बजाय Port<T> स्ट्रक्चर को होल्ड करने के लिए Vec<Rc<RefCell<Port<T>>>> में बदल दिया:

pub struct Port<T> {
    values: Vec<T>,
    ports: Vec<Rc<RefCell<Port<T>>>>,
}

इसलिए, मुझे iter(&self) विधि भी बदलनी पड़ी:

pub fn iter(&self) -> impl Iterator<Item = &T> {
    self.values.iter().chain(self.ports.iter().flat_map(|p| {
        Box::new(unsafe { (&*p.as_ref().as_ptr()).iter() }) as Box<dyn Iterator<Item = &T>>
    }))
}

मैं असुरक्षित कोड का उपयोग करने से बचने वाले किसी अन्य तरीके के बारे में नहीं सोच सका। इस कोड के साथ, मैं कर सकता हूँ:

  1. पोर्ट ए और बी बनाएं
  2. दोनों बंदरगाहों में फ़ीड मान
  3. चेन पोर्ट बी से पोर्ट ए
  4. डेटा के साथ पोर्ट बी खिलाते रहें
  5. पोर्ट ए पर पुनरावृति करें और सभी डेटा पढ़ें

मैंने एक उदाहरण के साथ एक खेल का मैदान बनाया है।

क्या आप ऐसा करने का कोई सुरक्षित तरीका जानते हैं?

0
Román Cárdenas 7 जून 2021, 18:56
यदि आपका कोई नया प्रश्न है, तो कृपया प्रश्न पूछें बटन पर क्लिक करके इसे पूछें। इस प्रश्न के लिए एक लिंक शामिल करें यदि यह संदर्भ प्रदान करने में मदद करता है। - समीक्षा से
 – 
Blastfurnace
7 जून 2021, 20:31
मैं केवल इतना कह सकता हूं कि यह unsafe कोड काफी गलत दिखता है और यह बग या क्रैश का स्रोत भी हो सकता है। आपको शायद एक अलग प्रश्न पूछने की ज़रूरत है, और अपने उपयोग के मामले को और विस्तार से समझाएं।
 – 
user4815162342
7 जून 2021, 21:07