تحويل integer إلى string في Rust: السحر بدون unwrap والماكرو

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

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

ابدأ الدورة

مقدمة: لماذا لا يمكنك ببساطة جمع رقم مع سلسلة نصية في Rust؟

\n\n

إذا كنت قادمًا من Python أو JavaScript، فأنت معتاد على كتابة شيء مثل str(42) أو 42 + "". في Rust، هذه الحيلة لن تنجح. المترجم سيسألك بصرامة: "أي نوع تريد الحصول عليه؟ Integer؟ String؟ قرر!"

\n\n

السؤال "How do I convert from an integer to a string?" هو واحد من أكثر الأسئلة شيوعًا على Stack Overflow في قسم Rust. آلاف المطورين يحيرون لماذا لا يتم تجميع كودهم عندما يحاولون كتابة:

\n\n
let number = 42;\nlet text = number; // خطأ: متوقع `&str`، تم العثور على `i32`
\n\n

اليوم سنلقي نظرة تحت غطاء هذا التحويل. ستتعلم ليس فقط كيف تفعل ذلك، ولكن أيضًا كيف يعمل على مستوى traits والتخصيصات وحتى لغة التجميع. هيا بنا!

\n\n

أسهل طريقة: دالة to_string()

\n\n

في Rust، كما في معظم اللغات الحديثة، تحتوي أنواع الأعداد الصحيحة على دالة to_string(). تبدو بسيطة بشكل مخادع:

\n\n
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

ماذا يحدث تحت الغطاء؟

\n\n

دالة to_string() ليست سحرًا من المترجم. إنها مطبقة من خلال trait Display. في المكتبة القياسية لـ Rust يوجد تطبيق:

\n\n
// نسخة مبسطة من المكتبة القياسية\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). كل استدعاء هو تخصيص للذاكرة. لمئات الآلاف من التحويلات، قد يصبح هذا عنق زجاجة في الأداء.

\n\n

بديل: ماكرو format!

\n\n

الطريقة الشائعة الثانية هي ماكرو format!. يعمل مثل println!، لكن بدلاً من الإخراج إلى وحدة التحكم، يعيد String:

\n\n
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. في الجوهر، هما نفس الشيء. الاختيار هو مسألة أسلوب وقابلية القراءة.

\n\n

متى تستخدم format!؟

\n
    \n
  • عندما تحتاج إلى إدراج رقم في منتصف سلسلة نصية: format!("الرقم: {}", num)
  • \n
  • عندما يكون التنسيق معقدًا (سداسي عشري، مع فواصل): format!("{:#x}", 255) → "0xff"
  • \n
  • عندما تكون تعبيرية الكود مهمة
  • \n
\n\n

trait Display مقابل ToString: أيهما أسرع؟

\n\n

إذا كنت تكتب مكتبة أو كودًا عالي الحمل، استخدم مباشرة trait Display من خلال write! في مخزن مؤقت. هذا يسمح بإعادة استخدام الذاكرة المخصصة بالفعل:

\n\n
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 في الحلقات التي تحتاج إلى تحويل الأرقام إلى سلاسل نصية بشكل متكرر، لأن تخصيص الذاكرة يحدث

المدونات

توصيات الكتب