Rust आपको सीधे s[0] लिखने की अनुमति क्यों नहीं देता?
\n\nआप अभी Rust सीखना शुरू कर रहे हैं, अपना पहला "Hello, world!" लिख रहे हैं और अचानक... एक कंपाइलेशन त्रुटि। आप स्ट्रिंग का पहला कैरेक्टर प्राप्त करने का प्रयास कर रहे हैं, जैसा कि आपने Python या JavaScript में सैकड़ों बार किया है, और कंपाइलर कुछ इस तरह फेंकता है the type 'String' cannot be indexed by '{integer}'। परिचित लगता है?
यह प्रश्न Stack Overflow पर सबसे लोकप्रिय प्रश्नों में से एक है। हजारों डेवलपर्स Rust की इस प्रतीत होने वाली "अजीबता" पर ठोकर खाते हैं। लेकिन वास्तव में इसके पीछे एक शानदार और सुविचारित समाधान है जो आपके कोड को मुश्किल से पकड़ में आने वाली बगों से बचाता है। इस लेख में, हम Rust में स्ट्रिंग इंडेक्सिंग करते समय शीर्ष गलतियों पर चर्चा करेंगे और, सबसे महत्वपूर्ण बात, इसे सही तरीके से करना सीखेंगे।
\n\nगलती #1: C या Python की तरह सीधी इंडेक्सिंग का प्रयास
\n\nसबसे पहली और सबसे आम गलती let c = my_string[0]; लिखने का प्रयास है। Rust में, स्ट्रिंग्स केवल ASCII कैरेक्टर की सरणियाँ नहीं हैं। वे UTF-8 एन्कोडिंग में संग्रहीत होती हैं। और यह सब कुछ बदल देता है।
यह काम क्यों नहीं करता?
\nकल्पना करें कि आपके पास एक स्ट्रिंग है "Привет"। इसमें 6 "कैरेक्टर" हैं (मानवीय दृष्टिकोण से)। लेकिन कंप्यूटर की मेमोरी में, यह 12 बाइट्स लेता है (सिरिलिक का प्रत्येक अक्षर 2 बाइट्स है)। यदि Rust my_string[0] की अनुमति देता, तो वह आपको क्या लौटाता? पहला बाइट (जो अपने आप में कुछ भी मायने नहीं रखता) या पहला कैरेक्टर? और यदि स्ट्रिंग में इमोजी होते, जो 4 बाइट्स लेते हैं? तो पूरी तरह से अव्यवस्था हो जाती।
Rust कंपाइलेशन स्तर पर सुरक्षा की गारंटी देता है। पूर्णांक द्वारा इंडेक्सिंग हमेशा O(1) में काम करती है और एक तत्व लौटाती है। लेकिन UTF-8 के लिए यह असंभव है: एक "कैरेक्टर" (char) 1 से 4 बाइट्स तक ले सकता है। इसलिए कंपाइलर बस ऐसे ऑपरेशन को प्रतिबंधित करता है, ताकि आप गलती से "कचरा" न पाएं या एन्कोडिंग को न तोड़ें।
\n\n// ❌ कंपाइलेशन त्रुटि!\nfn main() {\n let s = String::from("Привет");\n // let first = s[0]; // error[E0277]: the type 'String' cannot be indexed by '{integer}'\n println!("Привет, мир!");\n}\n\nगलती #2: कैरेक्टर सीमाओं पर विचार किए बिना स्लाइस (slicing) का भोला-भाला उपयोग
\n\nहाँ, Rust में स्लाइस काम करते हैं। आप &s[0..5] लिख सकते हैं। लेकिन यदि आप मल्टी-बाइट कैरेक्टर के "अंदर" चले जाते हैं, तो आपका प्रोग्राम रनटाइम पर पैनिक के साथ क्रैश हो जाएगा। इसे runtime panic कहा जाता है।
घातक त्रुटि का उदाहरण
\nfn main() {\n let s = String::from("Здравствуйте"); // 12 कैरेक्टर, लेकिन 24 बाइट्स\n // पहले 3 बाइट्स लेने का प्रयास करें\n let slice = &s[0..3]; // 'З' 2 बाइट्स लेता है, 'д' — 2 बाइट्स।\n // 3 बाइट्स दूसरे कैरेक्टर का आधा हिस्सा है!\n println!("{}", slice); // thread 'main' panicked at 'byte index 3 is not a char boundary'\n}\n\nयह केवल एक चेतावनी नहीं है। यह प्रोग्राम का पूर्ण क्रैश है। Rust मेमोरी के असुरक्षित उपयोग को माफ नहीं करता, रनटाइम में भी नहीं। यदि आप उपयोगकर्ता इनपुट (उपनाम, टिप्पणियाँ) के साथ काम करते हैं, तो आपको पहले सिरिलिक या एशियाई कैरेक्टर पर ऐसा panic मिलने की गारंटी है।
\n\nकैसे बचें: सुरक्षित रूप से सीमाएँ खोजने के लिए char_indices() विधि का उपयोग करें या chars() के माध्यम से काम करें।
गलती #3: बाइट्स, char और ग्रैफेम्स के बीच भ्रम
\n\nयह सबसे कपटी गलती है। भले ही आपने स्ट्रिंग से char प्राप्त करना सीख लिया हो, फिर भी आपको वह नहीं मिल सकता जिसकी आपने अपेक्षा की थी। क्योंकि Rust में char एक Unicode Scalar Value है। और जिसे मनुष्य "कैरेक्टर" मानता है (उदाहरण के लिए, "é" या "🇷🇺"), वह कई char से बना हो सकता है।
उदाहरण के साथ विश्लेषण
\nfn main() {\n let s = String::from("é"); // यह एक char (U+00E9) हो सकता है\n // या मिश्रित: 'e' (U+0065) + '◌́' (U+0301)\n let composed = String::from("e\\u{0301}"); // वही 'é', लेकिन दो char से\n println!("s की लंबाई: {} बाइट, {} char", s.len(), s.chars().count());\n println!("comp की लंबाई: {} बाइट, {} char", composed.len(), composed.chars().count());\n // दोनों एक जैसे दिखते हैं, लेकिन आंतरिक रूप से पूरी तरह से अलग हैं!\n}