Rust: دوال لغة Rust ونطاقها

آخر تحديث: 2026-08-26

تُعتبر الدوال عناصر من الدرجة الأولى في لغة Rust — حيث تُشكل أنواع معلماتها وقيمها المرجعة خط الدفاع الأول للمترجم.

تُعد الدوال اللبنات الأساسية للكود. تشبه صيغة الدوال في لغة Rust تلك المستخدمة في لغات عائلة C، لكنها تتميز بأسلوب فريد فيما يتعلق بـ مطابقة أنماط الوسيطات وقيم إرجاع التعبيرات.


1. ما ستتعلمه



2. قصة مقهى

(1) التحدي: لا توجد طريقة موحدة لتحضير القهوة

افتتحت ليزا مقهى، وأصبح العمل يزدهر أكثر فأكثر، لكن ظهرت بعض المشاكل أيضًا:

«سيكون من الرائع لو كان تحضير القهوة متسقًا تمامًا مثل استدعاء دالة: فالمدخلات نفسها تنتج دائمًا نفس النتائج.»

(2) نهج التعامل مع دوال Rust

RUST
// How to Make a Latte"Function":Input Parameters,Brew Coffee
fn make_latte(milk_ml: i32, coffee_ml: i32) -> String {
    let result = format!("Latte Complete:{}ml Milk + {}ml Coffee,Fragrant and Delicious!", milk_ml, coffee_ml);
    result  // No semicolons——Expression Return Value
}

fn main() {
    let my_coffee = make_latte(200, 30);
    println!("{}", my_coffee);

    let another = make_latte(150, 45);
    println!("{}", another);
}

الدوال تشبه وصفات القهوة: المعلمات هي المكونات (كمية الحليب، كمية القهوة)، والقيمة المرجعة هي المنتج النهائي. نفس المدخلات تضمن نفس المخرجات — وهذا هو الحتمية في الدوال.



3. تعريفات الدوال

(1) قواعد النحو الأساسية

RUST
fn function_name(param1: Type1, param2: Type2) -> ReturnType {
    // Function Body
    return_value  // Expression returns(No semicolons)
}
100%
graph TB
    A[fn Function Name] --> B[(List of Parameters)]
    A --> C[-> Return Type]
    A --> D[{ Function Body }]
    B --> E[Each parameter: name: Type]
    D --> F[Final expression = Return Value]
    D --> G[return Keywords = Return Early]

(2) الأشكال المختلفة للمعلمات وقيم الإرجاع

النموذج الصيغة المثال
لا توجد معلمات، ولا قيمة إرجاع fn foo() fn greet() { println!("hi"); }
تستقبل معلمات، ولا تُرجع أي قيمة fn foo(x: i32) fn show(n: i32) { println!("{}", n); }
مع معلمات وقيمة إرجاع fn foo(x: i32) -> i32 fn double(x: i32) -> i32 { x * 2 }
قيم إرجاع متعددة (توبول) fn foo() -> (i32, bool) fn stats() -> (i32, bool) { (42, true) }
العودة return value; if x < 0 { return 0; }

القاعدة الأساسية: التعبير الأخير في نص الدالة هو قيمة الإرجاع (بدون فاصلة منقوطة). استخدم الكلمة الرئيسية return للعودة المبكرة.

(3) طرق تمرير المعلمات

طريقة التمرير الصيغة تغيير الملكية السيناريوهات التي تنطبق عليها
التمرير بالقيمة fn foo(s: String) تنتقل الملكية إلى الدالة يتعين على الدالة استهلاك القيمة
التمرير بواسطة مرجع ثابت fn foo(s: &String) الاستعارة، مع بقاء الملكية دون تغيير للقراءة فقط
التمرير بالرجوع fn foo(s: &mut String) الاستعارة، مع بقاء الملكية دون تغيير يجب تعديل البيانات

(4) مقارنة قيم الإرجاع

الطريقة الصيغة تغيير الملكية مثال
عوائد التعبير عدم وجود فاصلة منقوطة في نهاية السطر نقل الملكية fn f() -> String { s }
العودة return value; نقل الملكية if err { return None; }
إرجاع مجموعات متعددة -> (T1, T2) عمليات نقل ملكية متعددة fn f() -> (i32, bool)
معلمة الإخراج &mut T لا يوجد نقل للملكية fn fill(buf: &mut Vec<i32>)


4. النطاق والتظليل

(1) نطاق الكتلة

يتم تعريف النطاق في لغة Rust وفقًا لـ {}. ولكل كتلة نطاقها الخاص بها:

RUST
fn main() {
    let x = 10;          // Outer Scope

    {
        let y = 20;      // Inner Scope,y Available only here
        println!("Inside: x={}, y={}", x, y);  // ✅ Can access the outer layer x
    }

    // println!("{}", y);  // ❌ Compilation Error:y Not in this scope

    println!("External: x={}", x);  // ✅
}

(2) التظليل المتغير

يمكن أن يُحجب تأثير المتغيرات الخارجية بواسطة متغيرات داخلية تحمل الاسم نفسه، ولا يمكن للكود الداخلي الوصول إلا إلى المتغيرات الداخلية:

RUST
fn main() {
    let x = 1;            // Outer layer x = 1

    {
        let x = 2;        // Inner layer x Covered the outer layer x
        println!("Inside x = {}", x);  // Output: Inside x = 2
    }

    println!("External x = {}", x);       // Output: External x = 1
}

(3) نطاق معلمات الدالة

تكون معلمات الدالة مرئية في جميع أنحاء نص الدالة بأكمله:

RUST
fn foo(x: i32) {
    // x Available throughout the entire function body
    let y = x + 1;
    println!("x={}, y={}", x, y);
}  // End of function, x and y are both destroyed


5. أمثلة على الوظائف

(1) ▶ المثال:توقيعات الدوال المتنوعة (الصعوبة ⭐)

RUST
// ============================================
// Functions with Different Combinations of Parameters and Return Types
// ============================================

// No parameters, no return value
fn print_separator() {
    println!("-------------------");
}

// Single-parameter functions with return values
fn square(x: i32) -> i32 {
    x * x  // No semicolons,Expression returns
}

// Multi-parameter
fn area(width: u32, height: u32) -> u32 {
    width * height
}

// Return multiple values(Tuple)
fn min_max(a: i32, b: i32) -> (i32, i32) {
    if a < b {
        (a, b)  // Tuples as Return Values
    } else {
        (b, a)
    }
}

fn main() {
    print_separator();
    println!("5 the square of: {}", square(5));
    println!("Area 10x20: {}", area(10, 20));

    let (min, max) = min_max(100, 50);
    println!("min={}, max={}", min, max);
}

الناتج:

TEXT 📖 للعرض فقط
-------------------
5 the square of: 25
Area 10x20: 200
min=50, max=100

min_max تُرجع مجموعة؛ وعند استدعائها، يتم تفكيكها وتعيين قيمها باستخدام let (min, max). وهذه هي الطريقة القياسية لـ«إرجاع قيم متعددة» في لغة Rust.


(2) ▶ المثال:العودة المبكرة ووضع الحراسة (مستوى الصعوبة ⭐⭐)

RUST
// ============================================
// Return Early:Inspection Criteria,If you're not satisfied, leave.
// ============================================

fn safe_divide(a: f64, b: f64) -> f64 {
    if b == 0.0 {
        println!("Error:The divisor cannot be 0");
        return f64::NAN;  // Return Early NaN(Not a Number)
    }
    a / b  // Under normal circumstances,Expression returns
}

fn get_grade(score: i32) -> &'static str {
    if score < 0 || score > 100 {
        return "Invalid Fractions";
    }
    if score >= 90 { return "A"; }
    if score >= 80 { return "B"; }
    if score >= 70 { return "C"; }
    if score >= 60 { return "D"; }
    "F"
}

fn main() {
    println!("10 / 3 = {}", safe_divide(10.0, 3.0));
    println!("10 / 0 = {}", safe_divide(10.0, 0.0));
    println!("Fractions 85: {}", get_grade(85));
    println!("Fractions -5: {}", get_grade(-5));
}

الناتج:

TEXT 📖 للعرض فقط
10 / 3 = 3.3333333333333335
Error:The divisor cannot be 0
10 / 0 = NaN
Fractions 85: B
Fractions -5: Invalid Fractions

يُستخدم «العودة المبكرة» (return) بشكل شائع في نمط «التحقق أولاً، ثم التنفيذ». ومن خلال معالجة حالات الخطأ والحالات الاستثنائية في البداية، تسير المنطقية اللاحقة بسلاسة ضمن «منطقة آمنة».


(3) ▶ المثال:التعبيرات الكتلية واختبار نطاقها (مستوى الصعوبة ⭐⭐)

RUST
// ============================================
// Scope and Return Values of Block Expressions
// ============================================

fn main() {
    let outer = "Outer-scope variables".to_string();

    let inner_result = {
        let inner = "Nested variables".to_string();
        println!("Accessible internally: {}", outer);  // ✅ The inner layer can access the outer layer

        // Shading Variables with the Same Name
        let outer = 42;
        println!("After masking outer: {}", outer);  // 42,Not a string

        inner.len()  // Block expression return values:inner length
    };  // inner Destroyed here

    println!("The value returned by the block: {}", inner_result);
    println!("External outer: {}", outer);  // ✅ Outer layer outer Still

    // println!("{}", inner);  // ❌ Compilation Error:inner Not in this scope
}

الناتج:

TEXT 📖 للعرض فقط
Accessible internally: Outer-scope variables
After masking outer: 42
The value returned by the block: 8
External outer: Outer-scope variables

قواعد نطاق الكتل: يمكن للكتل الداخلية الوصول إلى المتغيرات الخارجية، لكن الكتل الخارجية لا يمكنها الوصول إلى المتغيرات الداخلية. ولا يؤدي التظليل سوى إلى «إخفاء» المتغيرات الخارجية مؤقتًا؛ وبمجرد الخروج من الكتلة الداخلية، تعود المتغيرات الخارجية إلى حالتها الطبيعية.


(4) ▶ المثال:مؤشرات الدوال والدوال ذات الترتيب الأعلى (مستوى الصعوبة ⭐⭐⭐)

RUST
// ============================================
// Passing Functions as Arguments——An Introduction to Higher-Order Functions
// ============================================

type MathOp = fn(i32, i32) -> i32;

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

fn calculate(op: MathOp, a: i32, b: i32) -> i32 {
    op(a, b)
}

fn apply_twice(f: fn(i32) -> i32, x: i32) -> i32 {
    f(f(x))
}

fn main() {
    let result1 = calculate(add, 10, 5);
    let result2 = calculate(sub, 10, 5);
    let result3 = calculate(mul, 10, 5);
    println!("10 + 5 = {}", result1);
    println!("10 - 5 = {}", result2);
    println!("10 * 5 = {}", result3);

    let double = |x: i32| x * 2;
    let result4 = apply_twice(double, 3);
    println!("double(double(3)) = {}", result4);

    let increment = |x: i32| x + 1;
    let result5 = apply_twice(increment, 5);
    println!("increment(increment(5)) = {}", result5);

    let alice_points = 80;
    let bonus = |base: i32| -> i32 { base + 10 };
    println!("Alice After the bonus points are added: {}", bonus(alice_points));
}

الناتج:

TEXT 📖 للعرض فقط
10 + 5 = 15
10 - 5 = 5
10 * 5 = 50
double(double(3)) = 12
increment(increment(5)) = 7
Alice After the bonus points are added: 90

يمكن تمرير كل من مؤشرات الدوال (من النوع fn) والإغلاقات (من النوع |x| x * 2) كمعلمات إلى الدوال ذات الترتيب الأعلى. تشير مؤشرات الدوال إلى عناوين التعليمات البرمجية التي يتم تحديدها في وقت التحويل البرمجي، بينما يمكن للإغلاقات التقاط متغيرات البيئة.


▶ Example:Comprehensive Exercise—Processing Student Grades (Difficulty ⭐⭐⭐)

RUST
// ============================================
// Comprehensive Example:Function,Combining Scope and Shading
// ============================================

fn average(scores: &[i32]) -> f64 {
    if scores.is_empty() {
        return 0.0;
    }
    let sum: i32 = scores.iter().sum();
    sum as f64 / scores.len() as f64
}

fn classify(score: i32) -> &'static str {
    match score {
        90..=100 => "A",
        80..=89  => "B",
        70..=79  => "C",
        60..=69  => "D",
        _        => "F",
    }
}

fn report(names: &[&str], scores: &[i32]) {
    let avg = average(scores);
    println!("Class Average Score: {:.1}", avg);

    for (i, name) in names.iter().enumerate() {
        let score = scores[i];
        let grade = classify(score);
        let status = if score >= 60 { "Through" } else { "Failed" };
        println!("{}: {} pts, Level {}, {}", name, score, grade, status);
    }

    let passing = scores.iter().filter(|&&s| s >= 60).count();
    println!("Number of people who passed: {}/{}", passing, scores.len());
}

fn main() {
    let names = ["Alice", "Bob", "Charlie", "David"];
    let scores = [95, 72, 58, 88];

    println!("=== Report Card ===");
    report(&names, &scores);

    println!("\n--- Scope Demonstration ---");
    let result = {
        let scores = [100, 90, 80];
        let avg = average(&scores);
        format!("Average of the top three: {:.1}", avg)
    };
    println!("{}", result);

    let avg = "Outer layer avg";
    println!("Outer-scope variables: {}", avg);
}

الناتج:

TEXT 📖 للعرض فقط
=== Report Card ===
Class Average Score: 78.2
Alice: 95 pts, Level A, Through
Bob: 72 pts, Level C, Through
Charlie: 58 pts, Level F, Failed
David: 88 pts, Level B, Through
Number of people who passed: 3/4

--- Scope Demonstration ---
Average of the top three: 90.0
Outer-scope variables: Outer layer avg

يجمع هذا المثال بين استخدام تعريفات الدوال، ومعلمات الشرائح، وتصنيف match، وطرق المكرر، وإخفاء المتغيرات في نطاق الكتلة. ويوفر استخدام شريحة &[T] كمعلمة للدالة أقصى درجات المرونة، كما أن إخفاء متغير يحمل الاسم نفسه داخل نطاق الكتلة لا يؤثر على النطاق الخارجي.



❓ أسئلة شائعة

س لا يتضمن التعبير في السطر الأخير فاصلة منقوطة كقيمة إرجاع. ماذا أفعل إذا كنت أنسى أحيانًا إضافة الفاصلة المنقوطة؟
ج إذا نسيت الفاصلة المنقوطة، فسيصدر المُترجم خطأً، ويطلب منك تحديد ما إذا كان الأمر عبارة عن جملة أم تعبير.
س ما الفرق بين return والعودة من خلال تعبير؟
ج تُستخدم return للعودة المبكرة، بينما تُستخدم العودة من خلال تعبير في نهاية الدالة.
س لماذا يجب تحديد أنواع معلمات الدالة؟
ج لأن لغة Rust لا تستنتج أنواع معلمات الدالة.
س هل التظليل ممارسة سيئة؟
ج التظليل المعتدل ممارسة جيدة.
س هل يمكن تداخل الدوال؟
ج نعم، تدعم لغة Rust تعريف الدوال داخل دوال أخرى.

📖 ملخص


📝 تمارين

  1. الصعوبة ⭐: اكتب دالة fn is_even(n: i32) -> bool تحدد ما إذا كان عدد صحيح زوجيًا أم لا، ثم اكتب main لاستدعاء هذه الدالة وطباعة فئة كل عدد من 1 إلى 10.
  2. الصعوبة ⭐⭐: اكتب دالة fn max_of_three(a: i32, b: i32, c: i32) -> i32 تُرجع أكبر قيمة بين ثلاثة أعداد. (لا يجوز استخدام دالة .max() الموجودة في المكتبة القياسية؛ اكتب منطق المقارنة الخاص بك.)
  3. الصعوبة ⭐⭐⭐: اكتب دالة fn calculator(op: char, a: f64, b: f64) -> f64 تقوم بإجراء العملية المقابلة بناءً على المعلمات op ('+' '-' '*' '/')، وتُرجع f64::NAN عندما يكون المقسوم عليه 0. اختبر هذه العمليات الأربع في main.
Web-Tutorial.com

فريق Web-Tutorial التقني

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

100%