المتغيرات العامة في Rust: خرافات، واقع، وبدائل آمنة

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

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

ابدأ الدورة

مقدمة: "أين المتغيرات العامة؟" — الصدمة الأولى للمبتدئ في Rust

\n\n

لقد بدأت للتو في تعلم Rust، وكتبت بضعة برامج، وفجأة تدرك: أنت بحاجة إلى أن يكون متغير ما متاحًا من أي نقطة في الكود. في Python أو JavaScript، كنت ستكتب ببساطة global_var = 42 في المستوى الأعلى - وسيكون الأمر جاهزًا. لكن في Rust... المترجم ينظر إليك بتوبيخ ويصدر خطأ.

\n\n

السؤال "هل من الممكن استخدام المتغيرات العامة في Rust؟" هو أحد أكثر الأسئلة شيوعًا على Stack Overflow. وليس من دون سبب. Rust، على عكس العديد من اللغات، لا يسمح ببساطة بإعلان متغير عام. لماذا؟ لأن أمان الذاكرة ليس مجرد كلمة طنانة، بل هو أساس اللغة.

\n\n

في هذه المقالة، سنناقش جميع طرق العمل مع البيانات العامة في Rust: من الثوابت البسيطة إلى التخزين الآمن للخيوط. ستتعلم كيفية تجاوز قيود المترجم دون التضحية بالأمان، ومتى تكون المتغيرات العامة شرًا، ومتى تكون ضرورة.

\n\n

لماذا يمنع Rust المتغيرات العامة "العادية"؟

\n\n

قبل البحث عن حلول، دعنا نفهم المشكلة. في Rust، هناك قواعد صارمة لـ الملكية (ownership) و الاقتراض (borrowing). المتغير العام الذي يمكن تغييره من أي خيط في أي لحظة هو طريق مباشر إلى سباق البيانات (data race).

\n\n

تخيل: خيطان يحاولان في نفس الوقت كتابة قيمتين مختلفتين في نفس المتغير العام. النتيجة غير متوقعة. في C++ أو C، يؤدي هذا إلى سلوك غير محدد. Rust يضمن أن هذا ببساطة لن يتم تجميعه.

\n\n

لذلك، يطلب منا المترجم أن نحدد بوضوح:

\n
    \n
  • المتغير غير قابل للتغيير؟ استخدم const أو static.
  • \n
  • المتغير قابل للتغيير ويستخدم في خيط واحد؟ استخدم static mut (مع unsafe).
  • \n
  • المتغير قابل للتغيير ويستخدم في عدة خيوط؟ استخدم Mutex أو RwLock أو الأنواع الذرية.
  • \n
\n\n

الطريقة 1: الثوابت (const) — الخيار الأكثر أمانًا

\n\n

إذا كانت "قيمتك العامة" هي في الواقع ثابت لا يتغير أبدًا، استخدم const:

\n\n
const MAX_CONNECTIONS: u32 = 100;\nconst DATABASE_URL: &str = "postgresql://localhost:5432/mydb";\n\nfn main() {\n    println!("الحد الأقصى للاتصالات: {}", MAX_CONNECTIONS);\n}
\n\n

الثوابت في Rust ليست مجرد متغيرات. إنها تُضمّن (inline) بواسطة المترجم في مكان الاستخدام. أي، أثناء التجميع، سيتم استبدال MAX_CONNECTIONS بالرقم 100. هذا فعال، لكنه يعني أن الثابت ليس له عنوان ثابت في الذاكرة. إذا كنت بحاجة إلى موقع في الذاكرة (على سبيل المثال، لـ FFI)، استخدم static.

\n\n

الطريقة 2: المتغيرات الثابتة (static)

\n\n

إذا كنت بحاجة إلى متغير عام بعنوان ثابت في الذاكرة، استخدم static:

\n\n
static APP_NAME: &str = "تطبيق Rust الخاص بي";\nstatic VERSION: u32 = 1;\n\nfn main() {\n    println!("تشغيل {} الإصدار {}", APP_NAME, VERSION);\n}
\n\n

بشكل افتراضي، المتغيرات static غير قابلة للتغيير. هذا يضمن الأمان في بيئة متعددة الخيوط. ولكن ماذا لو كنت بحاجة إلى تغيير الحالة العامة؟

\n\n

الطريقة 3: المتغيرات الثابتة القابلة للتغيير (static mut) — بحذر، غير آمنة!

\n\n

يسمح Rust بإعلان متغير ثابت قابل للتغيير باستخدام static mut. لكن هناك مشكلة: أي قراءة أو كتابة لمثل هذا المتغير تتطلب كتلة unsafe.

\n\n
static mut COUNTER: u32 = 0;\n\nfn increment() {\n    unsafe {\n        COUNTER += 1;\n    }\n}\n\nfn main() {\n    increment();\n    unsafe {\n        println!("العداد: {}", COUNTER);\n    }\n}
\n\n

هام:

المدونات

توصيات الكتب