الانتقال إلى المحتوى الرئيسي
الوحدة 3 · الدرس 2 من 26/14 في الدورة~15 min
دروس الوحدة (2/2)

المراجع والاقتراض

في Rust، قد يكون نقل الملكية إلى الوظائف ذهابًا وإيابًا أمرًا شاقًا. لحل هذه المشكلة، يستخدم Rust المراجع (&).

يُسمى إنشاء مرجع لقيمة الاقتراض.

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

يتم الإعلان عن المرجع عن طريق وضع بادئة للنوع أو المتغير بالرمز &. بشكل افتراضي، تكون المراجع غير قابلة للتغيير: فهي تسمح بقراءة القيمة ولكن لا تسمح بتعديلها.

Rust
fn main() {
    let s1 = String::from("hello");

    // We pass a reference to s1, not ownership
    let len = calculate_length(&s1);

    // s1 is still usable here!
    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize { // s is a reference to a String
    s.len()
} // Here, s goes out of scope, but since it doesn't own the value, nothing happens

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

إذا كنت بحاجة إلى تعديل قيمة مستعارة، فيجب عليك استخدام مرجع قابل للتغيير مع &mut. يجب أيضًا تعريف المتغير الأصلي بأنه قابل للتغيير باستخدام mut:

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

    // We pass a mutable reference
    change(&mut s);

    println!("{}", s); // Prints "hello, world"
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

قواعد الاقتراض

لمنع تلف الذاكرة وسباق البيانات في وقت الترجمة، يفرض Rust قاعدتين أساسيتين:

  1. يمكنك الحصول على أي عدد من المراجع غير القابلة للتغيير (&T) لقيمة ما في نفس الوقت.
  2. أو يمكن أن يكون لديك مرجع واحد قابل للتغيير (&mut T) لقيمة ما في المرة الواحدة.

لا يمكنك مطلقًا الجمع بين المراجع غير القابلة للتغيير والمتغيرة لنفس القيمة في نفس النطاق:

Rust
let mut s = String::from("hello");

let r1 = &s; // Valid
let r2 = &s; // Valid
// let r3 = &mut s; // COMPILATION ERROR! You cannot create &mut s if s is already borrowed as immutable

النطاق المرجعي ومدة الحياة غير المعجمية (NLL)

تاريخيًا، استمر نطاق المرجع حتى نهاية الكتلة التي تم إنشاؤه فيها. اليوم، أصبح مترجم Rust أكثر ذكاءً بفضل Non-Lexical Lifetimes (NLL): ينتهي نطاق المرجع عند السطر الأخير الذي يتم استخدامه فيه بالفعل، وليس بالضرورة في نهاية الكتلة.

وهذا يجعل الكود التالي صالحًا تمامًا:

Rust
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
println!("{} and {}", r1, r2); // Last use of r1 and r2. The immutable references expire here!

let r3 = &mut s; // Valid! No active immutable references exist at this point

جربه بنفسك

تمرين#rust.m3.l2.e1
المحاولات: 0جارٍ التحميل…

قم بتمرير مرجع غير قابل للتغيير لـ s1 إلى دالة Calc_length باستخدام الرمز &.

جارٍ تحميل المحرر…
إظهار التلميح

استبدل `/* TODO \_/`بـ`&s1` لتمرير مرجع غير قابل للتغيير إلى السلسلة.

الحل متاح بعد 3 من المحاولات

تمرين#rust.m3.l2.e2
المحاولات: 0جارٍ التحميل…

اجعل المتغير s قابلاً للتغيير (let mut s) وقم بتمرير مرجع قابل للتغيير (&mut s) إلى دالة التغيير للسماح لها بتعديل السلسلة.

جارٍ تحميل المحرر…
إظهار التلميح

استخدم `let mut s` بدلاً من`let s`and call`change(&mut s);`.

الحل متاح بعد 3 من المحاولات

تمرين#rust.m3.l2.e3
المحاولات: 0جارٍ التحميل…

Declare a variable s containing String::from("Rust"). Create two distinct immutable references r1 and r2 to s, and finally print both references separated by a space using the println! macro.

جارٍ تحميل المحرر…
إظهار التلميح

Assign `let r1 = &s;` and `let r2 = &s;` to obtain two immutable references, then print them with `println!("{} {}", r1, r2);`.

الحل متاح بعد 3 من المحاولات