Rust में स्ट्रिंग पर इटरेशन: यह अंदरूनी रूप से कैसे काम करता है
\n\nक्या आपने कभी सोचा है कि जब आप for c in "Привет".chars() लिखते हैं तो वास्तव में क्या होता है? यदि आप Python या JavaScript से आए हैं, जहाँ स्ट्रिंग्स केवल कैरेक्टर के ऐरे होते हैं, तो Rust आपको एक सख्त शिक्षक की तरह लग सकता है जो आपको हर कार्य से पहले सोचने पर मजबूर करता है। और यह अच्छी बात है!
Stack Overflow पर "How do you iterate over a string by character" प्रश्न ने हज़ारों व्यूज़ प्राप्त किए हैं। और यह उचित भी है। Rust में कोई सरल string[i] नहीं है — और इसके अच्छे कारण हैं। आइए समझते हैं कि स्ट्रिंग को सही, कुशलतापूर्वक और बिना किसी आश्चर्य के कैसे इटरेट किया जाए।
Rust आपको इंडेक्स के आधार पर कैरेक्टर लेने की अनुमति क्यों नहीं देता?
\n\nइसका कारण UTF-8 एन्कोडिंग है। कई भाषाओं के विपरीत, Rust स्ट्रिंग्स को ASCII या UCS-2 में नहीं, बल्कि UTF-8 में संग्रहीत करता है। इसका मतलब है कि एक कैरेक्टर 1 से 4 बाइट तक का हो सकता है। उदाहरण के लिए:
\n\n- \n
'a'— 1 बाइट \n 'é'— 2 बाइट \n '中'— 3 बाइट \n '😊'— 4 बाइट \n
यदि Rust string[0] की अनुमति देता, तो "😊"[0] स्माइली नहीं, बल्कि उसके बाइट प्रतिनिधित्व का आधा हिस्सा — बेकार डेटा — लौटाता। इसलिए Rust कंपाइलर स्तर पर आपको ऐसी त्रुटियों से बचाता है।
स्ट्रिंग पर इटरेशन के मुख्य तरीके
\n\nRust तीन मुख्य इटरेशन विधियाँ प्रदान करता है, प्रत्येक का अपना उद्देश्य है। चुनाव इस बात पर निर्भर करता है कि आपको वास्तव में क्या चाहिए: कैरेक्टर, बाइट या ग्रैफेम।
\n\n1. कैरेक्टर (char) द्वारा इटरेशन
\n\nसबसे सामान्य तरीका .chars() विधि है। यह Unicode कैरेक्टर (प्रकार char) पर एक इटरेटर लौटाता है।
fn main() {\n let text = "Привет, мир! 🌍";\n \n for c in text.chars() {\n println!("{}", c);\n }\n \n // आउटपुट:\n // П\n // р\n // и\n // в\n // е\n // т\n // ,\n // (स्पेस)\n // м\n // и\n // р\n // !\n // (स्पेस)\n // 🌍\n}\n\nअंदरूनी रूप से, .chars() UTF-8 बाइट अनुक्रम को पार्स करता है और प्रत्येक सही कैरेक्टर लौटाता है। यदि स्ट्रिंग में गलत बाइट हैं, तो इटरेटर std::char::REPLACEMENT_CHARACTER ('�') लौटा सकता है।
महत्वपूर्ण: .chars() O(n) है। इटरेटर सभी बाइट्स से गुज़रता है, उन्हें चलते-फिरते डिकोड करता है। छोटी स्ट्रिंग्स के लिए यह तुरंत होता है, लेकिन लंबी स्ट्रिंग्स (मेगाबाइट टेक्स्ट) के लिए यह ध्यान देने योग्य हो सकता है।
2. बाइट (bytes) द्वारा इटरेशन
\n\nयदि आपको कच्चे बाइट चाहिए, तो .bytes() का उपयोग करें। यह एक तेज़ तरीका है, लेकिन आपको कैरेक्टर नहीं, बल्कि बाइट मिलते हैं।
fn main() {\n let text = "Hello";\n \n for byte in text.bytes() {\n println!("{}", byte);\n }\n \n // आउटपुट:\n // 72 (H)\n // 101 (e)\n // 108 (l)\n // 108 (l)\n // 111 (o)\n}\n\nअंग्रेज़ी टेक्स्ट के लिए, यह कैरेक्टर द्वारा इटरेशन के बराबर है। लेकिन रूसी या इमोजी के लिए ऐसा नहीं है। उदाहरण के लिए, "Привет" बाइट में 12 बाइट होगा (प्रति कैरेक्टर 2), और "🌍" — 4 बाइट।
कब उपयोग करें: जब आप बाइनरी डेटा, प्रोटोकॉल के साथ काम कर रहे हों, या UTF-8 डिकोडिंग के बिना अधिकतम प्रदर्शन चाहते हों।
\n\n3. ग्रैफेम क्लस्टर (grapheme clusters) द्वारा इटरेशन
\n\nयह और भी दिलचस्प है। कभी-कभी एक दृश्य "कैरेक्टर" कई Unicode कैरेक्टर से बना होता है। उदाहरण के लिए, किसी देश का झंडा या डायक्रिटिकल चिह्नों वाला अक्षर: "é" को एक कैरेक्टर é (U+00E9) या e + ◌́ (U+0065 + U+0301) के संयोजन के रूप में प्रस्तुत किया जा सकता है।
ग्रैफेम के साथ काम करने के लिए, आपको unicode-segmentation क्रेट को शामिल करना होगा:
// Cargo.toml\n// [dependencies]\n// unicode-segmentation = "1.10"\n\nuse unicode_segmentation::UnicodeSegmentation;\n\nfn main() {\n let text = "é";\n \n // chars() 1 स