الدرس1 / 3

المتغيرات والقابلية للتغيير

قراءة

اللي هتفهمه

  • ليه let لوحده بيمنع التعديل، وإيه اللي الكومبايلر بيكسبه من الشرط ده.
  • الفرق بين mut والظل — مش أسلوبين لنفس الحاجة، دول حاجتين مختلفتين.
  • إمتى تستخدم const، وإيه الفرق بينه وبين let.

الشرح

في أغلب اللغات، المتغير بيتغير — ده اسمه أصلاً. في Rust، let لوحده بيعمل ربط مش قابل للتغيير:

fn main() {
    let x = 5;
    println!("The value of x is: {x}");
    x = 6;
    println!("The value of x is: {x}");
}

الكومبايلر بيرفض ده. الرسالة بتقول إنك بتحاول تسند لمتغير غير قابل للتغيير مرتين، وبتوريك مكان الإسناد الأول، وبتقترح عليك تضيف mut:

error[E0384]: cannot assign twice to immutable variable `x`
 --> src/main.rs:4:5
  |
2 |     let x = 5;
  |         - first assignment to `x`
3 |     println!("The value of x is: {x}");
4 |     x = 6;
  |     ^^^^^ cannot assign twice to immutable variable
  |
help: consider making this binding mutable
  |
2 |     let mut x = 5;
  |         +++

rustc 1.97.1 (8bab26f4f 2026-07-14)

خد بالك من صيغة الرسالة: cannot assign **twice**. الكومبايلر مش بيمنعك من إن يكون للمتغير قيمة، هو بيمنعك من تغييرها بعد ما اتحطت.

ولو إنت فعلاً عايز تغيّرها، قول كده صراحة:

fn main() {
    let mut x = 5;
    println!("The value of x is: {x}");
    x = 6;
    println!("The value of x is: {x}");
}
افتح في Playground

المرئي

mut — خانة واحدةالظل — خانة جديدة كل مرةlet mut x = 5;5خانة واحدة اتعملتx = 6;6نفس الخانة، القيمة اتكتبت فوقهاx = x * 2;12نفس الخانة تاني — النوع ثابتlet x = 5;5خانة أولىlet x = x + 1;6خانة جديدة، الأولى مش موجودة بالاسم دهlet x = "ستة";"ستة"خانة تالتة — والنوع اتغيّر
على الشمال: mut بيعيد الكتابة في نفس الخانة، والنوع مقفول. على اليمين: كل let بيعمل خانة جديدة، فالنوع حر يتغيّر.

الكود

الظل — اسم قديم، خانة جديدة

فيه طريقة تانية “تغيّر” بيها متغير، وهي إنك تعرّفه من الأول:

fn main() {
    let x = 5;
    let x = x + 1;

    {
        let x = x * 2;
        println!("inner x = {x}");
    }

    println!("outer x = {x}");
}
افتح في Playground

الخرج:

inner x = 12
outer x = 6

ده مش تعديل — ده ربط جديد بنفس الاسم. الربط القديم لسه موجود في الذاكرة، بس مبقاش فيه طريقة توصله بالاسم ده. ولما القوس الداخلي قفل، الظل بتاعه انتهى وx رجع لقيمته 6.

الفرق اللي بيوضّح كل حاجة

الظل بيسمح بتغيير النوع، وmut لأ. ده اللي بيثبت إنهم مش حاجة واحدة بأسلوبين:

fn main() {
    let spaces = "   ";
    let spaces = spaces.len();
    println!("{spaces}");
}
افتح في Playground

ده بيشتغل وبيطبع 3. الأول &str، والتاني usize — نوعين مختلفين تماماً بنفس الاسم.

جرّب نفس الحاجة بـ mut:

fn main() {
    let mut spaces = "   ";
    spaces = spaces.len();
    println!("{spaces}");
}

الكومبايلر بيرفض، وبيقولك إنه كان متوقع &str — لأن ده نوع الخانة اللي اتعملت من أول سطر — ولقى usize:

error[E0308]: mismatched types
 --> src/main.rs:3:14
  |
2 |     let mut spaces = "   ";
  |                      ----- expected due to this value
3 |     spaces = spaces.len();
  |              ^^^^^^^^^^^^ expected `&str`, found `usize`

rustc 1.97.1 (8bab26f4f 2026-07-14)

mut بيقول “الخانة دي القيمة اللي جواها تتغيّر”. النوع بتاع الخانة نفسه اتحدد مرة واحدة ومش بيتغير أبداً.

const — حاجة تالتة خالص

const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;

fn main() {
    println!("{THREE_HOURS_IN_SECONDS}");
}
افتح في Playground

بيطبع 10800. تلات فروق عن let:

  • النوع إجباري. const X = 5; مش بيترجم.
  • القيمة بتتحسب وقت الترجمة. الضرب اللي فوق حصل وإنت بتترجم، والرقم 10800 اتحط في البرنامج جاهز.
  • ينفع في أي مكان، حتى برّه الدوال. let جوه دالة بس.

الاسم بحروف كبيرة وشرطة سفلية — ده عُرف متبع في كل كود Rust.

ليه بيشتغل كده

سؤال أهم من القواعد: إيه اللي الكومبايلر بيكسبه لما يبقى الافتراضي غير قابل للتغيير؟

الإجابة إنه بيعرف حاجات أكتر عن كودك، وكل حاجة بيعرفها بتفتحله باب تحسين أو ضمان أمان.

لو قيمة مش بتتغير، الكومبايلر يقدر يحسبها مرة واحدة، أو يحطها مباشرة في مكان استخدامها، أو ينسخها من غير قلق. والأهم من ده كله في Rust: يقدر يسمح لأكتر من مكان يقروا نفس البيانات في نفس الوقت — لأن مفيش حد هيغيّرها من ورا التاني.

القاعدة دي هي أساس منع سباق البيانات بين الـ threads، وهي نفسها اللي هتشوفها تاني في درس الاستعارة: قراءة متعددة مسموحة، كتابة واحدة حصرية. نفس الفكرة، مطبّقة على مستوى المتغير هنا وعلى مستوى الاستعارة هناك.

عشان كده mut مش “إذن”، هي إعلان. إنت بتقول للكومبايلر وللقارئ اللي جاي بعدك: الخانة دي بتتغير، خد بالك.

تمرين

اتوقع خرج البرنامج ده قبل ما تشغّله:

fn main() {
    let x = 10;
    let x = x * 2;

    {
        let x = x + 5;
        println!("1: {x}");
    }

    println!("2: {x}");

    let mut y = 1;
    y = y + 1;
    let y = y * 100;
    println!("3: {y}");
}
الحل
1: 25
2: 20
3: 200

السطر 1: x بقى 20 بالظل، وبعدين الظل الداخلي عمله 25. الظل ده جوه القوس بس.

السطر 2: القوس قفل، والظل الداخلي انتهى معاه. x رجع لـ 20 — مش 25.

السطر 3: y بدأ 1، اتغيّر لـ 2 بالتعديل العادي (mut)، وبعدين اتظلّل بقيمة 200. خد بالك إن الظل الأخير عمل ربط جديد وغير قابل للتغيير — لو حاولت تكتب y = 5; بعده، الكومبايلر هيرفض بـ E0384، رغم إن السطر اللي فوق كان فيه mut.

النقاش

لو عندك سؤال أو لقيت غلطة في الصفحة دي، اكتبها فيالنقاشات على GitHub. كل صفحة ليها موضوعها الخاص.