تخطَّ إلى المحتوى

🦀 شرح Rust

الدوال والتعابير

الدرس 2 من 28· ⏱ 4 دقائق قراءة· 🗓 آخر تحديث: ١٨ أكتوبر ٢٠٢٦

تعريف دالة

fn greet(name: &str) {
    println!("أهلًا {name}");
}

fn add(a: i32, b: i32) -> i32 {
    a + b
}

fn main() {
    greet("سارة");
    println!("{}", add(4, 6));   // 10
}
  • fn تعرّف الدالة، والأسماء بأسلوب snake_case (حروف صغيرة وشرطة سفلية).
  • نوع كل معامل إلزامي: Rust تستنتج أنواع المتغيّرات داخل الدوال، لكنها لا تخمّن تواقيع الدوال. التوقيع عقد واضح لمن يستدعيها.
  • -> i32 نوع القيمة المُرجعة. بلا سهم، الدالة تُرجع () (القيمة الفارغة، مثل void).
  • ترتيب الدوال في الملف لا يهمّ: يمكنك استدعاء دالة معرّفة بعد main.

التعبير الأخير هو القيمة المُرجعة

لاحظ أن add لا تحتوي return. في Rust، آخر تعبير في جسم الدالة بلا فاصلة منقوطة هو قيمتها:

fn square(x: i32) -> i32 {
    x * x          // بلا ; : هذه هي القيمة المُرجعة
}

وreturn موجودة، لكنها تُستخدم عادة للخروج المبكّر فقط:

fn safe_divide(a: i32, b: i32) -> Option<i32> {
    if b == 0 {
        return None;     // خروج مبكّر
    }
    Some(a / b)          // المسار العادي: التعبير الأخير
}

fn main() {
    println!("{:?} {:?}", safe_divide(10, 2), safe_divide(1, 0));   // Some(5) None
}

Option تعني "قيمة أو لا شيء"، وستفهمها بالتفصيل في درس Option وResult.

التعبير مقابل الجملة

هذا الفرق هو مفتاح فهم Rust:

  • التعبير (expression) يُنتج قيمة: 5 وa + b وadd(1, 2)، وحتى الكتلة { ... } وif وmatch.
  • الجملة (statement) تنفّذ شيئًا ولا تُنتج قيمة: let x = 5; جملة.

إضافة ; بعد تعبير تحوّله لجملة وتُسقط قيمته. ولأن الكتلة تعبير، يمكنك كتابة:

let total = {
    let price = 100;
    let tax = 16;
    price + tax        // قيمة الكتلة
};
println!("{total}");   // 116

والأمر نفسه مع if، فهي تعبير يُرجع قيمة:

let age = 20;
let label = if age >= 18 { "بالغ" } else { "قاصر" };
println!("{label}");

التفاصيل الكاملة للشروط والحلقات في درس التحكّم في التدفّق.

الفخّ الأشهر: فاصلة منقوطة زائدة

fn add(a: i32, b: i32) -> i32 {
    a + b;   // ❌ الفاصلة حوّلته لجملة، فالدالة تُرجع () لا i32
}

رسالة المترجم:

error[E0308]: mismatched types
 --> src/main.rs:1:27
  |
1 | fn add(a: i32, b: i32) -> i32 {
  |    ---                    ^^^ expected `i32`, found `()`
  |    |
  |    implicitly returns `()` as its body has no tail or `return` expression
2 |     a + b;
  |          - help: remove this semicolon to return this value

لاحظ السطر الأخير: مترجم Rust مشهور برسائل الخطأ الواضحة التي تقترح الحل غالبًا. اقرأ الرسالة كاملة قبل أن تبحث في الإنترنت.

عدّة قيم مُرجعة بصفّ (Tuple)

fn min_max(nums: &[i32]) -> (i32, i32) {
    let mut min = nums[0];
    let mut max = nums[0];
    for &n in nums {
        if n < min { min = n; }
        if n > max { max = n; }
    }
    (min, max)
}

fn main() {
    let (lo, hi) = min_max(&[4, 9, 1, 7]);
    println!("{lo} {hi}");   // 1 9
}

&[i32] شريحة: "أي مجموعة متجاورة من i32" سواء مصفوفة أو Vec (درس الشرائح). وتفكيك الصفّ في let (lo, hi) يعطي كل قيمة اسمًا.

المعاملات والملكية

حين تمرّر قيمة مثل String لدالة، تنتقل ملكيتها إليها ولا تعود قابلة للاستخدام عند المستدعي، إلا إذا مرّرت مرجعًا (&). هذا قلب Rust، وهو موضوع درس الملكية ودرس الاستعارة. لهذا ترى &str في greet أعلاه: الدالة تستعير النص ولا تأخذه.

أخطاء شائعة

  • ; بعد التعبير الأخير: خطأ expected i32, found (). احذفها.
  • نسيان نوع معامل: fn add(a, b) لا تُترجم؛ الأنواع إلزامية في التواقيع.
  • return x; في آخر الدالة: صحيحة لكنها غير اصطلاحية. اكتب x وحدها.
  • أسماء بأسلوب camelCase: تعمل، لكن المترجم يحذّرك؛ الاصطلاح snake_case للدوال والمتغيّرات.
  • تمرير String لدالة ثم استخدامها: خطأ "borrow of moved value". مرّر &s.

🎯 التالي: البنية و Cargo.

شرح الدوال والتعابير — Rust بالعربي
الدوال والتعابير — Rust بالعربي · The Code Fix

📚 لمزيد من التعمّق في Rust، راجِع كتاب Rust الرسمي.

هل كان هذا الدرس مفيدًا؟