शुरुआती Rust डेवलपर्स की शीर्ष गलतियाँ: उन्नत स्तर
\n\nRust एक ऐसी भाषा है जो सुरक्षित मेमोरी प्रबंधन और उच्च प्रदर्शन का वादा करती है। हालाँकि, जब आप बुनियादी उदाहरणों से वास्तविक प्रोजेक्ट्स की ओर बढ़ते हैं, तो ऐसी गलतियाँ सामने आने लगती हैं जो केवल & या mut जोड़ने से हल नहीं होतीं। इस लेख में, हम चार उन्नत जालों पर चर्चा करेंगे जिनमें अक्सर वे डेवलपर्स फँसते हैं जिन्होंने borrow checker और स्वामित्व की मूल बातें पहले ही सीख ली हैं।
यह लेख उन लोगों के लिए है जिन्होंने Rust में कई प्रोजेक्ट लिखे हैं और Rc, RefCell, ट्रेट्स और unsafe का सामना किया है। हम यह नहीं समझाएँगे कि उधार क्या है, बल्कि छिपी हुई समस्याओं पर ध्यान केंद्रित करेंगे।
1. Rc/RefCell में चक्रीय संदर्भों के माध्यम से मेमोरी लीक
\n\nसबसे घातक गलती यह सोचना है कि Rc स्वचालित रूप से लीक को रोकता है। वास्तव में, Rc संदर्भ गणना का उपयोग करता है, और यदि दो ऑब्जेक्ट एक-दूसरे को संदर्भित करते हैं, तो काउंटर कभी शून्य नहीं होगा। मेमोरी मुक्त नहीं होगी।
ग्राफ के साथ एक क्लासिक उदाहरण देखें:
\n\nuse std::rc::Rc;\nuse std::cell::RefCell;\n\n#[derive(Debug)]\nstruct Node {\n value: i32,\n children: Vec<Rc<RefCell<Node>>>,\n}\n\nfn main() {\n let a = Rc::new(RefCell::new(Node { value: 1, children: vec![] }));\n let b = Rc::new(RefCell::new(Node { value: 2, children: vec![] }));\n\n // चक्रीय संदर्भ बनाएँ\n a.borrow_mut().children.push(Rc::clone(&b));\n b.borrow_mut().children.push(Rc::clone(&a));\n\n // main से बाहर निकलने के बाद मेमोरी मुक्त नहीं होगी!\n drop(a);\n drop(b); // लीक: Rc सोचता है कि अभी भी और संदर्भ हैं\n}\n\n\nऐसा क्यों होता है? drop(a) के बाद a का संदर्भ काउंटर घटता है, लेकिन b में अभी भी children के माध्यम से a का संदर्भ है। इसी तरह b के लिए। अंततः दोनों ऑब्जेक्ट मेमोरी में रह जाते हैं।
समाधान: कमजोर संदर्भों के लिए Weak<T> का उपयोग करें। ग्राफ में, आमतौर पर एक नोड स्वामी (मजबूत संदर्भ) होता है, और बाकी कमजोर होते हैं।
use std::rc::{Rc, Weak};\nuse std::cell::RefCell;\n\n#[derive(Debug)]\nstruct Node {\n value: i32,\n parent: RefCell<Weak<Node>>, // माता-पिता के लिए कमजोर संदर्भ\n children: RefCell<Vec<Rc<Node>>>, // बच्चों के लिए मजबूत संदर्भ\n}\n\nfn main() {\n let leaf = Rc::new(Node {\n value: 3,\n parent: RefCell::new(Weak::new()),\n children: RefCell::new(vec![]),\n });\n\n let branch = Rc::new(Node {\n value: 5,\n parent: RefCell::new(Weak::new()),\n children: RefCell::new(vec![Rc::clone(&leaf)]),\n });\n\n *leaf.parent.borrow_mut() = Rc::downgrade(&branch);\n // अब branch हटाने पर leaf भी मुक्त हो जाएगा\n}\n\n\n2. स्व-संदर्भित संरचनाओं के लिए Pin का गलत उपयोग
\n\nवे संरचनाएँ जिनमें स्वयं के संदर्भ होते हैं (self-referential), अनिश्चित व्यवहार का एक सामान्य कारण हैं। Rust उन्हें सीधे बनाने की अनुमति नहीं देता, लेकिन Pin और unsafe के माध्यम से यह संभव है। गलती यह भूलना है कि Pin केवल यह गारंटी देता है कि ऑब्जेक्ट मेमोरी में स्थानांतरित नहीं होगा, लेकिन आंतरिक बदलावों से रक्षा नहीं करता।
फ्यूचर (future) के साथ उदाहरण:
\n\nuse std::pin::Pin;\nuse std::marker::PhantomPinned;\n\n#[derive(Debug)]\nstruct SelfReferential {\n data: String,\n pointer: *const String, // data पर कच्चा पॉइंटर\n _pin: PhantomPinned,\n}\n\nimpl SelfReferential {\n fn new(data: String) -> Self {\n SelfReferential {\n pointer: std::ptr::null(),\n data,\n _pin: PhantomPinned,\n }\n }\n\n fn init(self: Pin<&mut Self>) {\n let this = unsafe { self.get_unchecked_mut() };\n this.pointer = &this.data as *const String;\n }\n}\n