كيف يحول Rust البايتات إلى نصوص: الغوص تحت الغطاء

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

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

ابدأ الدورة

كيف يحول Rust البايتات إلى نصوص: الغوص تحت الغطاء



تخيل أنك استقبلت بيانات عبر الشبكة، أو قرأت ملفًا، أو فككت تشفير رسالة. أمامك مجرد مجموعة من الأرقام: vec![72, 101, 108, 108, 111]. إنها "Hello"! لكن Rust لن يسمح لك ببساطة بالقول: "أجل، هذا نص". لماذا؟ لأن Rust مثالي. لا يثق في البايتات حتى يتأكد من أنها UTF-8 صحيح.



سؤال "How do I convert a Vector of bytes (u8) to a string?" هو أحد أكثر الأسئلة شيوعًا على Stack Overflow. وليس بدون سبب. في هذه المقالة، لن نعرض فقط الكود الجاهز، بل سننظر تحت الغطاء: كيف يتحقق Rust من البايتات، وما الحفر المخفية التي تنتظرك، ولماذا unsafe ليس دائمًا شرًا.



لماذا Rust صارم جدًا؟



في Rust، النص (String أو &str) ليس مجرد مصفوفة من الأحرف. إنه تسلسل UTF-8 صالح مضمون. على عكس C أو Python حيث النص هو بايتات خام، يقدم Rust عقدًا: إذا كان هذا نصًا، فهو صحيح. هذا يحميك من أخطاء الترميز التي تطارد المطورين لعقود.



متجه البايتات Vec<u8> هو مجرد مجموعة من الأرقام. لا يعرف Rust ما قصدته: نصًا أم صورة أم بروتوكولًا ثنائيًا. لذلك، التحويل البسيط (cast) محظور. تحتاج إلى تحويل صريح.



الطريقة 1: الآمنة — String::from_utf8()



هذه أداتك الرئيسية. تأخذ الدالة Vec<u8> وتعيد Result<String, FromUtf8Error>.



fn main() {    let bytes = vec![72, 101, 108, 108, 111]; // "Hello"    match String::from_utf8(bytes) {        Ok(s) => println!("نجاح: {}", s),        Err(e) => println!("خطأ: {}", e),    }}


كيف يعمل هذا تحت الغطاء؟

  • يمر Rust عبر كل بايت ويتحقق مما إذا كان التسلسل يتوافق مع قواعد UTF-8.
  • إذا كان كل شيء على ما يرام — يقوم "بإعادة تغليف" الذاكرة: يصبح متجه البايتات نصًا. لا نسخ للبيانات! هذا تجريد بدون تكلفة (zero-cost abstraction).
  • إذا كان هناك خطأ — تحصل على Err يحتوي على المتجه الأصلي. يمكنك تحليله أو تجربة طريقة أخرى.


متى تستخدم؟ دائمًا عندما لا تكون متأكدًا من مصدر البيانات: إدخال المستخدم، حزم الشبكة، الملفات.



الطريقة 2: المتسامحة — String::from_utf8_lossy()



ماذا تفعل إذا كانت البايتات معيبة جزئيًا؟ على سبيل المثال، تقرأ ملفًا نصيًا قديمًا تسلل إليه بايت تالف. from_utf8 سيعيد خطأ. from_utf8_lossy سيعيد نصًا، مستبدلاً التسلسلات غير الصالحة برمز الاستبدال � (U+FFFD).



fn main() {    // 0xFF — بايت غير صالح في UTF-8 (إذا لم يكن جزءًا من تسلسل متعدد البايتات)    let bytes = vec![72, 101, 108, 108, 111, 0xFF];    let s = String::from_utf8_lossy(&bytes);    println!("{}", s); // سيطبع: Hello�}


اختلاف مهم: هذه الدالة تأخذ &[u8] (مرجع إلى شريحة)، وليس Vec<u8>. لا تستهلك ملكية المتجه. النتيجة هي Cow<'_, str> (نسخ عند الكتابة). إذا كانت البايتات صالحة — فهي مجرد مرجع للبيانات الأصلية. إذا لم تكن — يتم إنشاء نص جديد مع الاستبدالات.



متى تستخدم؟ عند التسجيل، التصحيح، العمل مع بيانات "مزعجة" حيث فقدان بضعة أحرف غير حرج.



الطريقة 3: السريعة والخطيرة — String::from_utf8_unchecked()



هذه طريقة unsafe. تأخذ ببساطة متجه البايتات وتقول: "حسنًا، أنا أثق بك، هذا نص". لا توجد أي تحققات. إذا أخطأت — ستحصل على سلوك غير محدد (undefined behavior). قد ينهار البرنامج، أو يدخل في حلقة لا نهائية، أو الأسوأ من ذلك، يعمل بشكل غير صحيح.



fn main() {\                

المدونات

توصيات الكتب