كيفية الحصول على &'static str من String في Rust: دليل كامل مع أمثلة

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

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

ابدأ الدورة

مقدمة: لماذا هذه مشكلة أصلاً؟

\n

إذا كنت قد كتبت أي شيء في Rust أكثر تعقيداً من "Hello, World!"، فمن المحتمل أنك واجهت موقفاً حيث يصرخ المترجم حرفياً: "expected &'static str, but found String". هل هذا مألوف؟ هذا أحد أكثر الأسئلة شيوعاً على Stack Overflow (عشرات الآلاف من المشاهدات)، وليس من دون سبب.

\n

الأمر أن String و &'static str هما نوعان مختلفان تماماً. String هو نوع مالك يمكن أن ينمو ويتغير أثناء التنفيذ. أما &'static str فهو مرجع لشريحة سلسلة تعيش طوال البرنامج. هذا يشبه مقارنة حقيبة بها أغراض (String) برف مثبت بإحكام (&'static str).

\n

في هذا الدليل، سنستعرض جميع الطرق القانونية لتحويل String إلى &'static str، من البسيطة إلى المتقدمة. نعم، سأريكم كيف لا تؤذوا أنفسكم في هذه العملية.

\n\n

ما هو &'static str في الواقع؟

\n

قبل أن نبدأ بكتابة الكود، دعنا نفهم النظرية. &'static str هو مرجع لسلسلة مضمون أنها تعيش طوال مدة تشغيل البرنامج. عادةً ما تكون هذه السلاسل مضمنة بشكل ثابت في الملف الثنائي:

\n
let hello: &'static str = "Hello, world!"; // يعيش للأبد\n
\n

ولكن ماذا لو تم إنشاء السلسلة ديناميكياً؟ على سبيل المثال، تُقرأ من ملف أو تأتي من الشبكة؟ هنا يبدأ الجزء المثير للاهتمام.

\n\n

الطريقة رقم 1: الأسهل — تسرب الذاكرة (نعم، هذا قانوني)

\n

نعم، لم تخطئ السمع. في Rust يوجد تسرب ذاكرة متعمد من خلال الدالة Box::leak. هذا ليس خطأ، بل ميزة! أنت تقول حرفياً للمترجم: "انسَ هذه الذاكرة، إنها ملكي إلى الأبد".

\n
use std::boxed::Box;\n\nfn string_to_static_str(s: String) -> &'static str {\n    // تحويل String إلى Box<str>، ثم "نسيانها"\n    let boxed_str: Box<str> = s.into_boxed_str();\n    // الآن هذه السلسلة ستعيش إلى الأبد\n    Box::leak(boxed_str)\n}\n\nfn main() {\n    let dynamic_string = String::from("لقد وُلدت ديناميكياً!");\n    let static_ref: &'static str = string_to_static_str(dynamic_string);\n    println!("{}", static_ref);\n    // لن يتم تحرير الذاكرة أبداً\n}\n
\n

الإيجابيات: بسيط، فعال، لا يتطلب كريتات خارجية.
\nالسلبيات: تفقد السيطرة على الذاكرة إلى الأبد. إذا استدعيت هذا في حلقة — مرحباً، OOM (نفاد الذاكرة).

\n\n

متى يكون هذا مناسباً؟

\n
    \n
  • عندما تعرف بالتأكيد أن السلسلة مطلوبة طوال عمر البرنامج (مثل الإعدادات التي تُحمّل مرة واحدة عند بدء التشغيل).
  • \n
  • في الأنظمة المضمنة حيث لا يوجد كومة وكل شيء ثابت.
  • \n
\n\n

الطريقة رقم 2: Lazy Static — للكسالى والأذكياء

\n

كريت lazy_static هو كلاسيكي في Rust. يسمح لك بإنشاء متغير ثابت يتم تهيئته فقط عند أول استخدام. مثالي للإعدادات والبيانات العامة.

\n
// أضف إلى Cargo.toml: lazy_static = "1.4"\nuse lazy_static::lazy_static;\n\nlazy_static! {\n    static ref MY_STRING: String = {\n        // لنفترض أننا نقرأ سلسلة من ملف\n        String::from("تم التحميل بتكاسل!")\n    };\n}\n\nfn get_static_str() -> &'static str {\n    // lazy_static يعطي تلقائياً &'static str\n    &MY_STRING\n}\n\nfn main() {\n    println!("{}", get_static_str());\n}\n
\n

الإيجابيات: \n

    \n
  • آمن (لا تسرب للذاكرة).
  • \n
  • تهيئة كسولة — يتم إنشاء السلسلة فقط عند الحاجة.
  • \n
  • يعمل مع أي أنواع، وليس فقط السلاسل.
  • \n
\nالسلبيات: \n
    \n
  • اعتماد على كريت خارجي.
  • \n
  • قد يكون مبالغاً فيه للحالات البسيطة.
  • \n
\n

\n\n

الطريقة رقم 3: OnceCell و OnceLock — البديل الحديث

\n

بدءاً من Rust 1.70، في المعيار

المدونات

توصيات الكتب