Rust में शून्य का वेक्टर: जितना लगता है उतना आसान नहीं
\n\nऐसा लगता है, शून्य से भरा वेक्टर बनाने से आसान क्या हो सकता है? Python में यह एक पंक्ति में किया जाता है: [0] * n। JavaScript में — Array(n).fill(0)। लेकिन जब आप Rust पर आते हैं, तो ऐसा सामान्य कार्य भी सिस्टम प्रोग्रामिंग, मेमोरी प्रबंधन और अमूर्तता पर शून्य लागत की एक पूरी दुनिया खोल देता है।
Stack Overflow पर प्रश्न "Creating a vector of zeros for a specific size" ने हजारों बार देखा गया है। और यह आकस्मिक नहीं है: एक सरल प्रश्न के पीछे Rust के मेमोरी, आरंभीकरण और सुरक्षा के साथ काम करने की गहरी समझ छिपी है। आज हम Rust के हुड के नीचे झांकेंगे और शून्य का वेक्टर बनाने के सभी तरीकों को समझेंगे, और सबसे महत्वपूर्ण बात, हम समझेंगे कि वास्तव में RAM में क्या होता है।
\n\nयह Python से अधिक जटिल क्यों है?
\n\nPython या JavaScript में, आप बस एक ऐरे बनाते हैं, और इंटरप्रेटर आपके लिए सारा गंदा काम करता है। Rust एक सिस्टम-स्तरीय भाषा है। यह मेमोरी को अप्रारंभीकृत (uninitialized) छोड़ने की अनुमति नहीं देता (जब तक कि आप unsafe का उपयोग नहीं करते)। प्रत्येक बाइट या तो स्पष्ट रूप से भरा जाना चाहिए, या "अपरिभाषित व्यवहार" के रूप में चिह्नित किया जाना चाहिए।
कल्पना करें: आप 100 खाली बक्से ऑर्डर करते हैं। Python में, आपको बस 100 खाली बक्से मिलेंगे। Rust में, कूरियर पूछेगा: "प्रत्येक के अंदर क्या रखूं?" — और यदि आप नहीं बताते, तो वह बस नहीं आएगा। यह गारंटी देता है कि आप कभी भी गलती से मेमोरी से "कचरा" नहीं पढ़ेंगे जो पिछले प्रोग्राम से बचा हुआ है।
\n\nविधि #1: क्लासिक vec![0; size]
\n\nसबसे स्पष्ट और अनुशंसित तरीका vec! मैक्रो का उपयोग करना है:
let zeros: Vec<i32> = vec![0; 10];\nprintln!("{:?}", zeros); // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n\nसंक्षिप्त, सुरुचिपूर्ण, स्पष्ट। लेकिन हुड के नीचे क्या होता है? आइए चरण दर चरण समझते हैं।
\n\nvec![0; 10] के हुड के नीचे
\n\nजब आप vec![0; 10] लिखते हैं, Rust निम्नलिखित करता है:
- \n
- मेमोरी आवंटित करता है हीप (heap) पर 10
i32प्रकार के तत्वों के लिए। प्रत्येकi324 बाइट लेता है, कुल 40 बाइट। \n - जाँच करता है कि प्रकार
Cloneट्रेट को लागू करता है। संख्याएँi32इसे लागू करती हैं, इसलिए कंपाइलर प्रत्येक तत्व के लिए मान0की प्रतिलिपि बना सकता है। \n - प्रतिलिपि लूप शुरू करता है
alloc::vec::from_elemके अंदर। कंपाइलर इस लूप कोmemsetमें अनुकूलित करता है (यदि प्रकार एक सरल अदिश है)। \n Vecलौटाता है जिसकी लंबाई 10 और क्षमता 10 है। \n
मुख्य बिंदु: मेमोरी अप्रारंभीकृत नहीं रहती। Rust गारंटी देता है कि वेक्टर तक पहुँचने से पहले प्रत्येक बाइट शून्य से भरा होगा।
\n\nविधि #2: vec![Default::default(); size]
\n\nयदि आपका प्रकार कोई संख्या नहीं है, लेकिन Default ट्रेट को लागू करता है, तो आप सामान्यीकृत दृष्टिकोण का उपयोग कर सकते हैं:
#[derive(Default)]\nstruct Point {\n x: f64,\n y: f64,\n}\n\nlet points: Vec<Point> = vec![Default::default(); 5];\nprintln!("{:?}", points[0]); // Point { x: 0.0, y: 0.0 }\n\nआदिम प्रकारों के लिए Default::default() 0, 0.0, false या '\\0' देता है। उपयोगकर्ता-परिभाषित संरचनाओं के लिए — वह जो आपने #[derive(Default)] में परिभाषित किया है।
प्रदर्शन के बारे में क्या?
\n\nआइए एक सरल बेंचमार्क लिखें (वास्तविक कोड में criterion का उपयोग करें):
use std::time::Instant;\n\nfn main() {\n let n = 1_000_000;\n\n let start = Instant::now();\n let v1 = vec![0u8; n];\n let dur1 = start.elapsed();\n\n let start = Instant::now();\n let v2 = vec![0u64; n];\n let dur2 = start.elapsed();\n\n println!("u8: {:?}", dur1);\n println!("u64: {:?}", dur2);\n}\n\nमें