التكرار عبر سلسلة نصية في Rust: كيفية التعامل مع الأحرف دون ألم

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

التكرار عبر سلسلة نصية في Rust: كيف يعمل تحت الغطاء



هل تساءلت يومًا عما يحدث حقًا عندما تكتب for c in "Привет".chars()؟ إذا كنت قادمًا من Python أو JavaScript، حيث السلاسل النصية هي مجرد مصفوفات من الأحرف، فقد يبدو لك Rust كمعلم صارم يجعلك تفكر قبل كل إجراء. وهذا شيء رائع!



سؤال "How do you iterate over a string by character" على Stack Overflow حصد آلاف المشاهدات. وليس من دون سبب. في Rust لا يوجد string[i] بسيط — وهناك أسباب وجيهة لذلك. دعنا نفهم كيفية التكرار عبر سلسلة نصية بشكل صحيح وفعال وبدون مفاجآت.



لماذا لا يسمح لك Rust بأخذ حرف ببساطة عن طريق الفهرس؟



الأمر كله يتعلق بـ ترميز UTF-8. على عكس العديد من اللغات، يخزن Rust السلاسل النصية في UTF-8، وليس ASCII أو UCS-2. هذا يعني أن الحرف الواحد يمكن أن يشغل من 1 إلى 4 بايت. على سبيل المثال:



  • 'a' — 1 بايت
  • 'é' — 2 بايت
  • '中' — 3 بايت
  • '😊' — 4 بايت


إذا كان Rust يسمح بـ string[0]، فإن "😊"[0] سيعيد ليس الرمز التعبيري، بل نصف تمثيله البايتي — بيانات غير مفيدة. لذلك يحميك Rust من مثل هذه الأخطاء على مستوى المترجم.



الطرق الأساسية للتكرار عبر سلسلة نصية



يقدم Rust ثلاث طرق رئيسية للتكرار، كل منها له غرضه الخاص. يعتمد الاختيار على ما تحتاجه بالضبط: أحرف أم بايتات أم graphemes.



1. التكرار عبر الأحرف (char)



الطريقة الأكثر شيوعًا هي .chars(). تقوم بإرجاع مكرر عبر أحرف Unicode (نوع char).



fn main() {    let text = "Привет, мир! 🌍";        for c in text.chars() {        println!("{}", c);    }        // الإخراج:    // П    // р    // и    // в    // е    // т    // ,    // (مسافة)    // м    // и    // р    // !    // (مسافة)    // 🌍}


تحت الغطاء، يقوم .chars() بتحليل تسلسل البايتات لـ UTF-8 ويعيد كل حرف صحيح. إذا كانت السلسلة تحتوي على بايتات غير صحيحة، فقد يعيد المكرر std::char::REPLACEMENT_CHARACTER ('�').



هام: .chars() هو O(n). يمر المكرر عبر جميع البايتات، مفككًا ترميزها أثناء التنقل. بالنسبة للسلاسل القصيرة، يكون هذا فوريًا، ولكن بالنسبة للسلاسل الطويلة (ميغابايتات من النص) قد يكون ملحوظًا.



2. التكرار عبر البايتات (bytes)



إذا كنت بحاجة إلى البايتات الخام، استخدم .bytes(). هذه طريقة سريعة، لكنك تحصل على بايتات وليس أحرفًا.



fn main() {    let text = "Hello";        for byte in text.bytes() {        println!("{}", byte);    }        // الإخراج:    // 72 (H)    // 101 (e)    // 108 (l)    // 108 (l)    // 111 (o)}


بالنسبة للنص الإنجليزي، هذا يعادل التكرار عبر الأحرف. لكن بالنسبة للروسية أو الرموز التعبيرية — لا. على سبيل المثال، "Привет" في البايتات سيكون 12 بايت (2 لكل حرف)، و "🌍" سيكون 4 بايتات.



متى تستخدم: عندما تعمل مع بيانات ثنائية أو بروتوكولات أو تريد أقصى أداء دون فك ترميز UTF-8.



3. التكرار عبر grapheme clusters



وهذا أكثر إثارة للاهتمام. أحيانًا "الحرف" الواحد بصريًا يتكون من عدة أحرف Unicode. على سبيل المثال، علم دولة أو حرف مع علامات تشكيلية: "é" يمكن تمثيله كحرف واحد é (U+00E9) أو كمجموعة e + ◌́ (U+0065 + U+0301).



للعمل مع graphemes، تحتاج إلى إضافة crate unicode-segmentation:



// Cargo.toml// [dependencies]// unicode-segmentation = "1.10"

use unicode_segmentation::UnicodeSegmentation;

fn main() { let text = "é"; // chars() سيعيد 1 ح

المدونات

توصيات الكتب