जंग आपको उन चीजों को करने से मना कर स्मृति सुरक्षा सुनिश्चित करने की कोशिश करता है जो संभावित रूप से असुरक्षित हो सकते हैं। चूंकि यह विश्लेषण संकलन-समय पर किया जाता है, इसलिए संकलक केवल कुशलता के एक उप-समूह के बारे में कारण बता सकता है जिसे सुरक्षित माना जाता है।
जंग में, आप आसानी संग्रहीत कर सकती है या तो माता-पिता के लिए एक संदर्भ (मूल उधार, इस प्रकार उत्परिवर्तन रोकने के द्वारा) या बच्चे नोड्स की सूची (जो आप अधिक स्वतंत्रता देता है, उन्हें मालिक द्वारा), लेकिन दोनों नहीं (unsafe
का उपयोग किए बिना)। यह addNode
के कार्यान्वयन के लिए विशेष रूप से समस्याग्रस्त है, जिसके लिए दिए गए नोड के माता-पिता को उत्परिवर्तनीय पहुंच की आवश्यकता होती है। हालांकि, यदि आप parent
पॉइंटर को एक परिवर्तनीय संदर्भ के रूप में संग्रहीत करते हैं, तो, क्योंकि किसी विशेष ऑब्जेक्ट का केवल एक ही विस्थापन संदर्भ एक समय में उपयोग योग्य हो सकता है, माता-पिता तक पहुंचने का एकमात्र तरीका एक बच्चे नोड के माध्यम से होगा, और आप चाहते हैं केवल एक ही बच्चे नोड रखने में सक्षम हो, अन्यथा आपके पास एक ही पैरेंट नोड के दो उत्परिवर्तनीय संदर्भ होंगे।
यदि आप असुरक्षित कोड से बचना चाहते हैं, तो कई विकल्प हैं, लेकिन उन्हें सभी को कुछ बलिदान की आवश्यकता होगी।
सबसे आसान समाधान बस parent
क्षेत्र को दूर करने के लिए है। हम नोड के माता-पिता को याद रखने के लिए एक अलग डेटा संरचना को परिभाषित कर सकते हैं, जबकि हम नोड में इसे संग्रहीत करने के बजाए पेड़ को पार करते हैं।
पहले, आइए Node
को परिभाषित करते हैं:
#[derive(Debug)]
struct Node<T> {
data: T,
children: Vec<Node<T>>,
}
impl<T> Node<T> {
fn new(data: T) -> Node<T> {
Node { data: data, children: vec![] }
}
fn add_child(&mut self, child: Node<T>) {
self.children.push(child);
}
}
(मैं एक data
क्षेत्र जोड़ा क्योंकि एक पेड़ नोड्स पर डेटा के बिना सुपर उपयोगी नहीं है!)
चलो अब के रूप में हम नेविगेट माता-पिता को ट्रैक करने के लिए एक और struct को परिभाषित करते हैं:
#[derive(Debug)]
struct NavigableNode<'a, T: 'a> {
node: &'a Node<T>,
parent: Option<&'a NavigableNode<'a, T>>,
}
impl<'a, T> NavigableNode<'a, T> {
fn child(&self, index: usize) -> NavigableNode<T> {
NavigableNode {
node: &self.node.children[index],
parent: Some(self)
}
}
}
impl<T> Node<T> {
fn navigate<'a>(&'a self) -> NavigableNode<T> {
NavigableNode { node: self, parent: None }
}
}
यह समाधान ठीक काम करता है अगर तुम पेड़ उत्परिवर्तित के रूप में आप यह नेविगेट और आप माता-पिता रख सकते की जरूरत नहीं है NavigableNode
ऑब्जेक्ट्स (जो एक रिकर्सिव एल्गोरिदम के लिए ठीक काम करता है, लेकिन अगर आप NavigableNode
को किसी अन्य डेटा स्ट्रक्चर में स्टोर करना चाहते हैं और इसे चारों ओर रखते हैं तो बहुत अच्छा काम नहीं करता है)। दूसरे प्रतिबंध को माता-पिता को स्टोर करने के लिए उधारित सूचक के अलावा कुछ और उपयोग करके कम किया जा सकता है; यदि आप अधिकतम genericity चाहते हैं, आप Borrow
trait का उपयोग प्रत्यक्ष मूल्यों, उधार संकेत दिए गए, Box
तों, Rc
की, आदि
अब अनुमति देने के लिए, के zippers के बारे में बात कर सकते हैं। कार्यात्मक प्रोग्रामिंग में, ज़िप्पर का उपयोग डेटा संरचना (सूची, पेड़, मानचित्र इत्यादि) के किसी विशेष तत्व पर "फोकस" करने के लिए किया जाता है ताकि उस तत्व को एक्सेस करने में स्थिर समय लगे, जबकि उस डेटा संरचना के सभी डेटा को बनाए रखा जाए। यदि आपको अपने पेड़ पर नेविगेट करने की आवश्यकता है और नेविगेशन के दौरान को उत्परिवर्तित करने के दौरान, पेड़ को नेविगेट करने की क्षमता को बनाए रखने के दौरान, तो आप एक पेपर को एक जिपर में बदल सकते हैं और जिपर के माध्यम से संशोधन कर सकते हैं।
#[derive(Debug)]
struct NodeZipper<T> {
node: Node<T>,
parent: Option<Box<NodeZipper<T>>>,
index_in_parent: usize,
}
impl<T> NodeZipper<T> {
fn child(mut self, index: usize) -> NodeZipper<T> {
// Remove the specified child from the node's children.
// A NodeZipper shouldn't let its users inspect its parent,
// since we mutate the parents
// to move the focused nodes out of their list of children.
// We use swap_remove() for efficiency.
let child = self.node.children.swap_remove(index);
// Return a new NodeZipper focused on the specified child.
NodeZipper {
node: child,
parent: Some(Box::new(self)),
index_in_parent: index,
}
}
fn parent(self) -> NodeZipper<T> {
// Destructure this NodeZipper
let NodeZipper { node, parent, index_in_parent } = self;
// Destructure the parent NodeZipper
let NodeZipper {
node: mut parent_node,
parent: parent_parent,
index_in_parent: parent_index_in_parent,
} = *parent.unwrap();
// Insert the node of this NodeZipper back in its parent.
// Since we used swap_remove() to remove the child,
// we need to do the opposite of that.
parent_node.children.push(node);
let len = parent_node.children.len();
parent_node.children.swap(index_in_parent, len - 1);
// Return a new NodeZipper focused on the parent.
NodeZipper {
node: parent_node,
parent: parent_parent,
index_in_parent: parent_index_in_parent,
}
}
fn finish(mut self) -> Node<T> {
while let Some(_) = self.parent {
self = self.parent();
}
self.node
}
}
impl<T> Node<T> {
fn zipper(self) -> NodeZipper<T> {
NodeZipper { node: self, parent: None, index_in_parent: 0 }
}
}
इस ज़िपर का उपयोग करने के लिए आपको पेड़ की जड़ नोड का स्वामित्व है, की जरूरत है:
यहाँ कैसे हम Node
ऊपर परिभाषित के लिए एक ज़िपर को लागू कर सकता है। नोड्स के स्वामित्व को लेकर, जिपर कॉपी या क्लोनिंग नोड्स से बचने के लिए चीजों को चारों ओर स्थानांतरित कर सकता है। जब हम एक जिपर ले जाते हैं, तो हम वास्तव में पुराने जिपर को छोड़ देते हैं और एक नया बनाते हैं (हालांकि हम self
को म्यूट करके इसे भी कर सकते हैं, लेकिन मैंने सोचा कि यह स्पष्ट था, साथ ही यह आपको चेन विधि कॉल करने देता है)।
ऊपर दिए गए विकल्पों संतोषजनक नहीं हैं, और आप पूरी तरह एक नोड में एक नोड के माता-पिता की दुकान चाहिए, तो अगला सबसे अच्छा विकल्प Rc<RefCell<Node<T>>>
उपयोग करने के लिए माता पिता के लिए और बच्चों को संदर्भित करने के लिए है। Rc
साझा स्वामित्व को सक्षम बनाता है, लेकिन रनटाइम पर संदर्भ गणना करने के लिए ओवरहेड जोड़ता है। RefCell
आंतरिक परिवर्तनशीलता को सक्षम बनाता है, लेकिन रनटाइम पर सक्रिय बोरो का ट्रैक रखने के लिए ओवरहेड जोड़ता है। और RefCell
का उपयोग कर कार्यान्वयन के लिए See DK.'s answer।
आपका प्रश्न क्या है? – Shepmaster
स्वामित्व आपके वर्तमान उदाहरण में प्रकारों के माध्यम से व्यक्त नहीं किया गया है, क्या आप 'std :: vector> बच्चों को लिखना चाहते थे;'?इसके अलावा, विनाशक 'आभासी' क्यों है? क्या यह एक इंटरफेस होना चाहिए? –
नोड अपने बच्चों का मालिक है और माता-पिता के स्वामित्व में है। मैं nesessary की तुलना में अधिक टेम्पलेट्स का उपयोग नहीं करना चाहता था। – nulleight