Rust: المراجع والاستعارات في لغة Rust

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

الاقتباس يشبه بطاقة المكتبة — يمكنك استعارته (اقتباس للقراءة فقط) أو طلب إذن حصري لإعادة صياغته (اقتباس قابل للتعديل)، لكن لا يمكنك القيام بالأمرين معًا في الوقت نفسه.

تتيح آلية المراجع في لغة Rust الوصول إلى البيانات دون نقل الملكية. ويُطلق على ذلك اسم «الاستعارة».


1. ما ستتعلمه



2. الرسوم التخطيطية المفاهيمية

100%
flowchart LR
    subgraph "Transfer of Ownership"
        A1["Data String"] -->|"let s2 = s1<br>move"| B1["New Owner s2"]
        B1 -->|"s1 Failure"| C1["Compilation Error<br>println!(s1)"]
    end
    subgraph "Citation, Borrowing"
        A2["Data String"] -->|"let r = &s<br>borrow"| B2["Quote r"]
        B2 -->|"r Return after use<br>s Still valid"| C2["✅ Secure Access"]
    end


3. قصة مكتبة

(1) حسرة: تمزق الكتاب إلى أشلاء

يوجد في مكتبة «توم» كتاب شهير بعنوان البرمجة بلغة Rust، ولا يمكن استعارته إلا مرة واحدة:

«ليتني أستطيع أن أسمح لعدة أشخاص بقراءة نفس الكتاب في الوقت نفسه دون أن يمزقوه إربًا...»

(2) حلول لمراجع Rust

RUST
fn main() {
    let book = String::from("Rust Programming");

    // Multiple immutable references: Multiple people at the same time "read-only" check out
    let reader1 = &book;   // Reader 1 checks out (read-only)
    let reader2 = &book;   // Reader 2 checks out (read-only)
    println!("Readers 1 See: {}", reader1);
    println!("Readers 2 See: {}", reader2);
    // Both references are valid at the same time -- because everyone has read-only access

    let mut notebook = String::from("Notebook");

    // Mutable References: write-only permission
    let writer = &mut notebook;  // Only one person can make changes
    writer.push_str("-- I wrote some notes");
    println!("Rewriter: {}", writer);
    // writer ends scope here, only then can others borrow it again.
}

قواعد الاقتباس تشبه قواعد المكتبة: إما أن يتمكن عدة أشخاص من القراءة في الوقت نفسه (متعدد &T)، أو أن يتمكن شخص واحد من التحرير بمفرده (واحد &mut T) — لكن لا يمكن أن يحدث الأمران في آن واحد.



4. قواعد الاستعارة

100%
graph TB
    A[Borrowing Rules] --> B[You can only choose one of them.]
    B --> C[Multiple immutable references &T]
    B --> D[Or a mutable reference &mut T]
    B --> E[Cannot coexist]
    A --> F[References must always be valid.]
    F --> G[There must be no dangling references.]
القاعدة الوصف عواقب المخالفة
عدة &T أو &mut T واحد لا يمكن أن تتواجد المراجع الثابتة والقابلة للتغيير معًا خطأ في الترجمة
يجب أن تكون المراجع صالحة لا يمكن أن تستمر صلاحية المرجع بعد انتهاء صلاحية الكائن الذي يشير إليه خطأ في الترجمة
عندما تكون المرجعية غير قابلة للتغيير، لا يمكن تعديل القيمة الأصلية أيضًا في حالة وجود &T، لا يمكن تعديل القيمة الأصلية عبر &mut T خطأ في التحويل البرمجي
نطاق NLL ينتهي نطاق &mut T بعد آخر استخدام له تحسين يعتمد على المُجمِّع

(2) مقارنة أنواع المراجع

نوع الاقتباس الصيغة الأذونات الحد الأقصى المسموح به الاستخدامات الشائعة
مرجع ثابت &T للقراءة فقط متعدد وصول للقراءة فقط إلى معلمات الدالة
مرجع المتغير &mut T قراءة/كتابة 1 معلمة الدالة تُعدِّل البيانات
مؤشر فات (شريحة) &[T] للقراءة فقط متعددة شريحة مصفوفة/سلسلة

(3) مرجع سريع لسيناريوهات الاقتراض

السيناريو النهج الموصى به مثال
الوصول إلى الوظائف للقراءة فقط &T fn len(s: &String) -> usize
الدوال التي تُعدِّل البيانات &mut T fn push(s: &mut String, ch: char)
استخدام بيانات الدالة T (التمرير بالقيمة) fn consume(s: String)
بيانات إرجاع الدالة T (قيمة الإرجاع) fn create() -> String
وظائف متعددة تشترك في الوصول للقراءة فقط &T وظائف متعددة تتلقى نفس &T
التعديلات المتناوبة &mut T (التعدد الزمني) تتيح تقنية NLL إنشاء &mut T بالتناوب


5. أمثلة على الاستشهادات

(1) ▶ المثال:المراجع الثابتة — الوصول للقراءة فقط (مستوى الصعوبة ⭐⭐)

RUST
// ============================================
// Immutable References &T: Multiple readers can borrow items at the same time
// ============================================

fn calculate_length(s: &String) -> usize {
    // s is a String reference, no ownership
    s.len()
}  // s goes out of scope, but since it's a reference, it will not destroy the String

fn main() {
    let s = String::from("Hello, Rust!");

    let len = calculate_length(&s);  // Borrow s, no transfer of ownership
    println!("'{}' The length of is: {}", s, len);  // ✅ s Still available

    // Multiple immutable references can coexist
    let r1 = &s;
    let r2 = &s;
    println!("r1: {}, r2: {}", r1, r2);  // ✅ Read simultaneously
}

الناتج:

TEXT 📖 للعرض فقط
'Hello, Rust!' The length of is: 12
r1: Hello, Rust!, r2: Hello, Rust!

&s تنشئ مرجعًا إلى s؛ وعند تمريرها إلى دالة، لا يتم نقل الملكية. وبعد عودة الدالة، يصبح المرجع غير صالح، لكن s لا تزال موجودة. وهذا ما يُعرف بـ«الاستعارة» — أي أنك تستخدمها ثم تعيدها.


(2) ▶ المثال:المراجع القابلة للتغيير — أذونات التعديل الحصرية (مستوى الصعوبة ⭐⭐)

RUST
// ============================================
// Mutable References &mut T: Only one writer at a time
// ============================================

fn main() {
    let mut s = String::from("Hello");

    // Create a mutable reference
    let r = &mut s;
    r.push_str(", world!");
    println!("Modifying Through a Variable Reference: {}", r);
    // r is used here for the last time, NLL ends

    // After the mutable reference ends, you can create a new mutable reference.
    let r2 = &mut s;
    r2.push_str("!!");
    println!("Another mutable reference: {}", r2);

    // --- Examples of Errors (Uncomment to view compilation errors) ---
    // let mut s2 = String::from("test");
    // let ref1 = &mut s2;
    // let ref2 = &mut s2;  // ❌ There cannot be two mutable references at the same time.
    // println!("{}, {}", ref1, ref2);
}

الناتج:

TEXT 📖 للعرض فقط
Modifying Through a Variable Reference: Hello, world!
Another mutable reference: Hello, world!!!

&mut قابلة للتعديل — أي يمكن تعديل قيمة المرجع. ومع ذلك، تفرض لغة Rust قاعدة مفادها أنه لا يمكن وجود سوى مرجع واحد قابل للتعديل في كل مرة. وهذا يمنع حدوث تضارب في الوصول إلى البيانات — دون الحاجة إلى أقفال أو عمليات ذرية؛ حيث يتم حل المشكلة بالكامل في مرحلة الترجمة.


(3) ▶ المثال:لا يمكن أن تتعايش المراجع الثابتة والمتغيرة (الصعوبة ⭐⭐⭐)

RUST
// ============================================
// Demonstration of Conflicts Between Immutable and Mutable References
// ============================================

fn main() {
    let mut data = String::from("Key Data");

    let r1 = &data;       // ✅ Immutable References 1
    let r2 = &data;       // ✅ Immutable References 2
    println!("r1: {}, r2: {}", r1, r2);
    // r1 and r2 are used here for the last time

    let r3 = &mut data;   // ✅ At this point, you can create a mutable reference.
    r3.push_str("--Modified");
    println!("r3: {}", r3);

    // --- Examples of Errors: A mutable reference cannot be created while an immutable reference still exists. ---
    // let mut s = String::from("Demo");
    // let r_a = &s;        // Immutable References
    // let r_b = &mut s;    // ❌ Compilation Error: immutable reference already exists
    // println!("{}, {}", r_a, r_b);
}

الناتج:

TEXT 📖 للعرض فقط
r1: Key Data, r2: Key Data
r3: Key Data--Modified

النقطة الأساسية: تخرج المرجعان غير القابلان للتغيير r1 وr2 من نطاقهما بعد آخر استخدام لهما (println!)، وعندها فقط يمكن إنشاء المرجع القابل للتغيير r3. وهذا هو NLL (Non-Lexical Lifetime) — حيث يحدد المُجمِّع بذكاء متى تصبح المرجعية غير مستخدمة، بدلاً من انتظار نهاية الكتلة.


(4) ▶ المثال:منع حدوث «المراجع المعلقة» (مستوى الصعوبة ⭐⭐⭐)

RUST
// ============================================
// How Do Compilers Prevent Dangling References?
// ============================================

// Examples of Errors: Return a reference to a local variable
// fn dangle() -> &String {
//     let s = String::from("hello");
//     &s  // ❌ Compilation Error: s is destroyed at the end of the function, the reference would be dangling
// }

// The Correct Approach: Return String directly (transfer of ownership)
fn no_dangle() -> String {
    let s = String::from("hello");
    s  // Return String directly, ownership is transferred to the caller
}

fn main() {
    let s = no_dangle();
    println!("Safe Return Values: {}", s);

    // The Correct Way to Reference Local Variables: Use within the scope
    let local = String::from("Local Data");
    let r = &local;          // Quote
    println!("Referencing Local Variables: {}", r);
    // r ends here, local is still valid
}  // local is destroyed here (later than r), safe

الناتج:

TEXT 📖 للعرض فقط
Safe Return Values: hello
Referencing Local Variables: Local Data

تحدث «الإشارة المعلقة» عندما يتم تحرير الذاكرة التي تشير إليها إحدى الإشارات. يمكن لمُترجم لغة Rust اكتشاف هذه الحالة في مرحلة الترجمة — فإذا تم تدمير الكائن الذي تشير إليه إحدى الإشارات بعد إنشاء هذه الإشارة، فسيُبلغ المُترجم عن وجود خطأ. وهذا يقضي تمامًا على الأخطاء مثل «المؤشرات المعلقة».


(5) ▶ المثال:تمرين شامل — معاملات الحساب المصرفي (مستوى الصعوبة ⭐⭐⭐)

RUST
// ============================================
// Comprehensive Example: Applications of References in Real-Life Scenarios
// ============================================

struct Account {
    owner: String,
    balance: f64,
}

fn check_balance(account: &Account) -> String {
    format!("{} balance: {:.2} yuan", account.owner, account.balance)
}

fn deposit(account: &mut Account, amount: f64) {
    if amount <= 0.0 {
        println!("The deposit amount must be greater than 0");
        return;
    }
    account.balance += amount;
    println!("Deposit {:.2} Yuan Chenggong", amount);
}

fn transfer(from: &mut Account, to: &mut Account, amount: f64) -> bool {
    if amount <= 0.0 || from.balance < amount {
        println!("Transfer Failed: Insufficient balance or invalid amount");
        return false;
    }
    from.balance -= amount;
    to.balance += amount;
    println!("Transfer {:.2} yuan: {} -> {}", amount, from.owner, to.owner);
    true
}

fn show_accounts(accounts: &[Account]) {
    println!("--- Account List ---");
    for (i, acc) in accounts.iter().enumerate() {
        println!("{}. {} balance: {:.2} yuan", i + 1, acc.owner, acc.balance);
    }
}

fn main() {
    let mut alice = Account { owner: String::from("Alice"), balance: 1000.0 };
    let mut bob = Account { owner: String::from("Bob"), balance: 500.0 };

    println!("{}", check_balance(&alice));
    println!("{}", check_balance(&bob));

    deposit(&mut alice, 200.0);
    println!("{}", check_balance(&alice));

    transfer(&mut alice, &mut bob, 300.0)?;

    let accounts = [&alice, &bob];
    show_accounts(&accounts);

    let total: f64 = accounts.iter().map(|a| a.balance).sum();
    println!("Total Deposits: {:.2} yuan", total);
}

الناتج:

TEXT 📖 للعرض فقط
Alice balance: 1000.00 yuan
Bob balance: 500.00 yuan
Deposit 200.00 Yuan Successful
Alice balance: 1200.00 yuan
Transfer 300.00 yuan: Alice -> Bob
--- Account List ---
1. Alice balance: 900.00 yuan
2. Bob balance: 800.00 yuan
Total Deposits: 1700.00 yuan

يوضح هذا المثال التطبيق العملي لأنواع المراجع الثلاثة: &T (استعلام للقراءة فقط)، و&mut T (عملية تعديل)، و&[T] (تجول للقراءة فقط). يتطلب transfer مرجعين من نوع &mut T في آن واحد، وهو أمر آمن في لغة Rust لأنهما يشيران إلى بيانات مختلفة.



❓ أسئلة شائعة

س ما الفرق بين المرجع والمؤشر؟
ج المرجع هو مؤشر يُضمن صلاحيته.
س هل يمكن أن تتواجد &T و&mut T معًا؟
ج لا.
س ما المقصود بـ NLL (Non-Lexical Lifetime)؟
ج تتيح NLL انتهاء صلاحية المراجع بعد آخر استخدام لها، بدلاً من الانتظار حتى نهاية الكتلة.
س متى يجب استخدام المرجع بدلاً من التمرير بالملكية؟
ج استخدم المرجع عندما تحتاج فقط إلى «إلقاء نظرة» على البيانات، واستخدم التمرير بالملكية عندما تحتاج إلى «امتلاك» البيانات أو «الاحتفاظ بها» على المدى الطويل.
س هل يمكن تخزين مرجع في بنية (struct)؟
ج نعم، ولكن عليك تحديد معلمة مدة الصلاحية.

📖 ملخص


📝 تمارين

  1. الصعوبة ⭐: اكتب دالة fn print_message(msg: &String) تعرض السلسلة التي تُمرر إليها. في main، أنشئ String ثم استدعِ هذه الدالة للتحقق من أن المتغير الأصلي لا يزال متاحًا بعد الاستدعاء.
  2. الصعوبة ⭐⭐: اكتب برنامجًا لإنشاء mut String. أولاً، أنشئ مرجعين غير قابلين للتعديل واعرضهما؛ ثم أنشئ مرجعًا قابلاً للتعديل، وقم بتعديل محتواه، واعرض النتيجة المعدلة.
  3. الصعوبة ⭐⭐⭐: حاول كتابة دالة تُرجع مرجعًا (مثل fn get_ref() -> &String)، ولاحظ رسائل الخطأ التي يعرضها المُترجم. ثم عدّلها بحيث تُرجع ملكية String، وافهم سبب منع لغة Rust إرجاع المراجع إلى المتغيرات المحلية.
Web-Tutorial.com

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

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

100%