«كيف يمكنني التحويل بين String و &str و Vec<u8> و &[u8]؟» — قائمة التحقق الخاصة بك لتحسين الأداء في Rust
\n\nإذا كنت قد كتبت يومًا كودًا بلغة Rust يتعامل مع النصوص أو البايتات، فمن المحتمل أنك واجهت السؤال: «كيف أحول String إلى &str؟ و Vec<u8> إلى &[u8]؟ والعكس؟». هذا أحد أكثر الأسئلة شيوعًا على Stack Overflow (عشرات الآلاف من المشاهدات)، وليس من دون سبب. فالتحويلات التي تبدو بسيطة تخفي مخاطر خفية، خاصة عندما يتعلق الأمر بالأداء.
في هذه المقالة، لن نقتصر على سرد طرق «كيف تفعل». بل سنشرح لماذا بعض الطرق أسرع من غيرها، ومتى يجب استخدام كل منها. ستتعلم كيفية تجنب النسخ غير الضروري، والتخصيصات الزائدة، وكيفية كتابة كود فعال حقًا في Rust.
\n\nلماذا تحتاج إلى معرفة هذا؟ السياق والأداء
\nRust هي لغة تمنحك التحكم في الذاكرة. ولكن مع التحكم تأتي المسؤولية. التحويل بين السلاسل النصية والبايتات هو أحد أكثر العمليات شيوعًا. على سبيل المثال، عند العمل مع بروتوكولات الشبكة، أو تسلسل البيانات (JSON، Protobuf)، أو مجرد قراءة الملفات. اختيار التحويل الخاطئ قد يؤدي إلى نسخ بيانات كان بإمكانك استخدامها دون نسخ. في عالم البرمجة النظامية، هذا أمر بالغ الأهمية.
\n\nدعنا نحلل الأنواع الأربعة الرئيسية ونتعلم كيفية التنقل بينها دون خسائر.
\n\nString و &str: وجهان لعملة واحدة
\nString هي سلسلة نصية مالكة وقابلة للتعديل ومشفرة بـ UTF-8. &str هي شريحة (slice) مستعارة وغير قابلة للتعديل. القاعدة الأساسية: String هي مالك البيانات، &str هي مرجع للبيانات.
كيف تحصل على &str من String (والعكس)
\nهذه هي الحالة الأبسط والأكثر أمانًا.
\n\nlet my_string = String::from("مرحبًا بالعالم!");\n// 1. الحصول على &str من String (مجرد استعارة)\nlet my_str: &str = &my_string;\n// أو باستخدام as_str()\nlet my_str2: &str = my_string.as_str();\n\n// 2. الحصول على String من &str (إنشاء نسخة جديدة)\nlet new_string: String = my_str.to_string();\n// أو\nlet new_string2: String = String::from(my_str);\n\nهام: تحويل &str إلى String دائمًا ما ينشئ ذاكرة جديدة مخصصة (تخصيص). هذا مكلف. تحويل String إلى &str هو مجرد أخذ مرجع (مجاني).
كيف تحصل على Vec<u8> من String (والعكس)
\nهنا يبدأ الجزء المثير للاهتمام، لأن String داخليًا هي مجرد Vec<u8> مع ضمان بصحة UTF-8.
let my_string = String::from("Rust");\n\n// 3. String -> Vec<u8>\n// الخيار أ (النسخ): آمن ولكنه مكلف\nlet bytes_vec_copy: Vec<u8> = my_string.as_bytes().to_vec();\n\n// الخيار ب (بدون نسخ، استهلاك String):\nlet bytes_vec_owned: Vec<u8> = my_string.into_bytes(); // لم يعد my_string موجودًا!\n\n// 4. Vec<u8> -> String (التحقق من صحة UTF-8)\nlet new_string: String = String::from_utf8(bytes_vec_owned).expect("UTF-8 غير صالح");\n// إذا كنت متأكدًا من الصحة:\nlet new_string_unchecked: String = unsafe { String::from_utf8_unchecked(bytes_vec_copy) };\n\nالنقطة الأساسية: into_bytes() هو تحويل بدون تكلفة. يأخذ المخزن المؤقت الداخلي Vec<u8> ويسلمه لك دون نسخ بايت واحد. as_bytes().to_vec() هو نسخ صريح.
طريقة from_utf8_unchecked هي unsafe. استخدمها فقط إذا كنت متأكدًا بنسبة 100% أن البايتات هي UTF-8 صالح. وإلا، فقد يتعطل البرنامج مع ذعر (panic) أو ما هو أسوأ، سلوك غير محدد (UB).