مقدمة: "أين المتغيرات العامة؟" — الصدمة الأولى للمبتدئ في Rust
\n\nلقد بدأت للتو في تعلم Rust، وكتبت بضعة برامج، وفجأة تدرك: أنت بحاجة إلى أن يكون متغير ما متاحًا من أي نقطة في الكود. في Python أو JavaScript، كنت ستكتب ببساطة global_var = 42 في المستوى الأعلى - وسيكون الأمر جاهزًا. لكن في Rust... المترجم ينظر إليك بتوبيخ ويصدر خطأ.
السؤال "هل من الممكن استخدام المتغيرات العامة في 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
الطريقة 1: الثوابت (const) — الخيار الأكثر أمانًا
\n\nإذا كانت "قيمتك العامة" هي في الواقع ثابت لا يتغير أبدًا، استخدم const:
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.
الطريقة 2: المتغيرات الثابتة (static)
\n\nإذا كنت بحاجة إلى متغير عام بعنوان ثابت في الذاكرة، استخدم static:
static APP_NAME: &str = "تطبيق Rust الخاص بي";\nstatic VERSION: u32 = 1;\n\nfn main() {\n println!("تشغيل {} الإصدار {}", APP_NAME, VERSION);\n}\n\nبشكل افتراضي، المتغيرات static غير قابلة للتغيير. هذا يضمن الأمان في بيئة متعددة الخيوط. ولكن ماذا لو كنت بحاجة إلى تغيير الحالة العامة؟
الطريقة 3: المتغيرات الثابتة القابلة للتغيير (static mut) — بحذر، غير آمنة!
\n\nيسمح Rust بإعلان متغير ثابت قابل للتغيير باستخدام static mut. لكن هناك مشكلة: أي قراءة أو كتابة لمثل هذا المتغير تتطلب كتلة unsafe.
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هام: