مقدمة: لماذا لا يمكنك ببساطة جمع رقم مع سلسلة نصية في Rust؟
\n\nإذا كنت قادمًا من Python أو JavaScript، فأنت معتاد على كتابة شيء مثل str(42) أو 42 + "". في Rust، هذه الحيلة لن تنجح. المترجم سيسألك بصرامة: "أي نوع تريد الحصول عليه؟ Integer؟ String؟ قرر!"
السؤال "How do I convert from an integer to a string?" هو واحد من أكثر الأسئلة شيوعًا على Stack Overflow في قسم Rust. آلاف المطورين يحيرون لماذا لا يتم تجميع كودهم عندما يحاولون كتابة:
\n\nlet number = 42;\nlet text = number; // خطأ: متوقع `&str`، تم العثور على `i32`\n\nاليوم سنلقي نظرة تحت غطاء هذا التحويل. ستتعلم ليس فقط كيف تفعل ذلك، ولكن أيضًا كيف يعمل على مستوى traits والتخصيصات وحتى لغة التجميع. هيا بنا!
\n\nأسهل طريقة: دالة to_string()
\n\nفي Rust، كما في معظم اللغات الحديثة، تحتوي أنواع الأعداد الصحيحة على دالة to_string(). تبدو بسيطة بشكل مخادع:
fn main() {\n let num: i32 = 255;\n let s: String = num.to_string();\n println!("{}", s); // سيطبع: "255"\n}\n\nهذا يعمل مع جميع أنواع الأعداد الصحيحة: i8، u32، i64، usize، إلخ.
ماذا يحدث تحت الغطاء؟
\n\nدالة to_string() ليست سحرًا من المترجم. إنها مطبقة من خلال trait Display. في المكتبة القياسية لـ Rust يوجد تطبيق:
// نسخة مبسطة من المكتبة القياسية\nimpl<T: fmt::Display> ToString for T {\n #[inline]\n fn to_string(&self) -> String {\n use fmt::Write;\n let mut buf = String::new();\n // هنا يتم استدعاء دالة fmt() من trait Display\n let _ = buf.write_fmt(format_args!("{}", self));\n buf.shrink_to_fit(); // تحسين الذاكرة\n buf\n }\n}\n\nالنقطة الأساسية: to_string() تنشئ String جديدة على الكومة (heap). كل استدعاء هو تخصيص للذاكرة. لمئات الآلاف من التحويلات، قد يصبح هذا عنق زجاجة في الأداء.
بديل: ماكرو format!
\n\nالطريقة الشائعة الثانية هي ماكرو format!. يعمل مثل println!، لكن بدلاً من الإخراج إلى وحدة التحكم، يعيد String:
fn main() {\n let num = 1024;\n let s = format!("{}", num);\n println!("طول السلسلة: {}", s.len()); // 4\n}\n\nالفرق بين to_string() و format! ضئيل. to_string() داخليًا تستدعي format_args! و write_fmt. في الجوهر، هما نفس الشيء. الاختيار هو مسألة أسلوب وقابلية القراءة.
متى تستخدم format!؟
\n- \n
- عندما تحتاج إلى إدراج رقم في منتصف سلسلة نصية:
format!("الرقم: {}", num)\n - عندما يكون التنسيق معقدًا (سداسي عشري، مع فواصل):
format!("{:#x}", 255)→ "0xff" \n - عندما تكون تعبيرية الكود مهمة \n
trait Display مقابل ToString: أيهما أسرع؟
\n\nإذا كنت تكتب مكتبة أو كودًا عالي الحمل، استخدم مباشرة trait Display من خلال write! في مخزن مؤقت. هذا يسمح بإعادة استخدام الذاكرة المخصصة بالفعل:
use std::fmt::Write;\n\nfn main() {\n let num = 42;\n let mut buffer = String::with_capacity(20); // تخصيص الذاكرة مسبقًا\n\n // كتابة الرقم في المخزن المؤقت\n write!(buffer, "{}", num).unwrap();\n println!("{}", buffer); // "42"\n\n // مسح واستخدام مرة أخرى\n buffer.clear();\n write!(buffer, "{}", 256).unwrap();\n println!("{}", buffer); // "256"\n}\n\nهذا النهج يعطي زيادة في الأداء تصل إلى 2-3x في الحلقات التي تحتاج إلى تحويل الأرقام إلى سلاسل نصية بشكل متكرر، لأن تخصيص الذاكرة يحدث