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

المؤشرات الذكية: Box وRc

المؤشر الذكي هو بنية بيانات تعمل مثل المؤشر، ولكنها تحتوي على بيانات وصفية وإمكانيات إضافية (مثل حساب المراجع أو إدارة الذاكرة تلقائيًا).

في Rust، تستعير المراجع العادية (مثل &) البيانات فقط. من ناحية أخرى، غالبًا ما تمتلك المؤشرات الذكية البيانات التي تشير إليها. تتضمن الأمثلة الشائعة String وVec<T>، لكن المكتبة القياسية توفر مكتبة أخرى متخصصة.


Box<T> لتخصيص الكومة

أبسط مؤشر ذكي هو Box<T>، الذي يقوم بتخزين البيانات في الكومة بدلاً من المكدس. يبقى فقط المؤشر إلى البيانات المخصصة للكومة على المكدس.

يتم استخدامه بشكل رئيسي في الحالات التالية:

  1. عندما يكون لديك نوع لا يمكن معرفة حجمه في وقت الترجمة (مثل النوع العودي)، وتريد استخدام قيمة من هذا النوع في سياق يتطلب حجمًا دقيقًا.
  2. عندما تريد نقل ملكية كمية كبيرة من البيانات دون نسخها.

مثال أساسي:

Rust
fn main() {
    let b = Box::new(5); // allocates the value 5 on the heap
    println!("b = {}", *b); // dereferences with * to read the value
}

أنواع العودية وقوائم السلبيات

النوع العودي هو نوع يمكن أن يحتوي على قيمة خاصة به كجزء من تعريفه. يجب أن يعرف الصدأ الحجم الدقيق للنوع في وقت الترجمة. نظرًا لأن عمق النوع العودي يمكن أن يكون لا نهائيًا، فإن المترجم يُنشئ خطأ ما لم تستخدم صندوقًا (له حجم ثابت، لأنه مؤشر):

Rust
enum List {
    Cons(i32, Box<List>),
    Nil,
}

Rc<T>: العد المرجعي

هناك حالات حيث يمكن أن يكون لقيمة واحدة مالكين متعددين. يقوم المؤشر الذكي Rc<T> (تم حساب المرجع) بتتبع عدد المراجع إلى القيمة لتحديد ما إذا كانت القيمة لا تزال قيد الاستخدام. إذا انخفض العدد إلى الصفر، فسيتم إلغاء تخصيص المورد بأمان.

[!ملاحظة] Rc<T> مناسب فقط للسيناريوهات ذات الترابط الواحد. بالنسبة للسيناريوهات متعددة الخيوط (المتزامنة)، يجب عليك استخدام Arc<T> (تم حساب المرجع الذري).

Rust
use std::rc::Rc;

fn main() {
    let a = Rc::new(5);
    let b = Rc::clone(&a); // increments the reference count to 2
    println!("References: {}", Rc::strong_count(&a));
}

جربه بنفسك

التمرين 1: تخصيص الكومة مع الصندوق

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

قم بتخصيص قيمة عددية 42 على الكومة باستخدام Box::new وقم بتعيينها لمتغير يسمى val. بعد ذلك، قم بطباعة القيمة المخزنة في Box عن طريق إلغاء الإشارة إليها بشكل صريح باستخدام عامل التشغيل *.

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

استخدم `let val = Box::new(42);` لتخزين القيمة في الكومة وإلغاء الإشارة إليها باستخدام `*val` داخل `println!`.

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

التمرين 2: بنية البيانات العودية (قائمة السلبيات)

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

حدد تعدادًا متكررًا يسمى List يحتوي على متغيرين: Cons الذي يلتف صفًا (i32, Box<List>)، وNil الذي يمثل نهاية القائمة. في الوظيفة الرئيسية، قم بإنشاء قائمة مرتبطة تحتوي على العنصر 1 متبوعًا بـ Nil.

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

تم تعريف التعداد كـ `enum List { Cons(i32, Box<List>), Nil }`. أنشئ القائمة باستخدام `List::Cons(1, Box::new(List::Nil))`.

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

التمرين 3: مشاركة الملكية مع Rc

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

استخدم std::rc::Rc لإنشاء عدد صحيح مشترك يحتوي على القيمة 100 في المتغير a. انسخ المرجع إلى متغير b باستخدام Rc::clone. وأخيرًا، قم بطباعة عدد المراجع النشطة باستخدام الدالة Rc::strong_count.

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

قم بإنشاء القيمة باستخدام `Rc::new(100)`. استنسخ باستخدام `Rc::clone(&a)`واعرض عدد المالك باستخدام`Rc::strong_count(&a)`.

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