دروس الوحدة (2/2)
المؤشرات الذكية: Box وRc
المؤشر الذكي هو بنية بيانات تعمل مثل المؤشر، ولكنها تحتوي على بيانات وصفية وإمكانيات إضافية (مثل حساب المراجع أو إدارة الذاكرة تلقائيًا).
في Rust، تستعير المراجع العادية (مثل &) البيانات فقط. من ناحية أخرى، غالبًا ما تمتلك المؤشرات الذكية البيانات التي تشير إليها. تتضمن الأمثلة الشائعة String وVec<T>، لكن المكتبة القياسية توفر مكتبة أخرى متخصصة.
Box<T> لتخصيص الكومة
أبسط مؤشر ذكي هو Box<T>، الذي يقوم بتخزين البيانات في الكومة بدلاً من المكدس. يبقى فقط المؤشر إلى البيانات المخصصة للكومة على المكدس.
يتم استخدامه بشكل رئيسي في الحالات التالية:
- عندما يكون لديك نوع لا يمكن معرفة حجمه في وقت الترجمة (مثل النوع العودي)، وتريد استخدام قيمة من هذا النوع في سياق يتطلب حجمًا دقيقًا.
- عندما تريد نقل ملكية كمية كبيرة من البيانات دون نسخها.
مثال أساسي:
fn main() {
let b = Box::new(5); // allocates the value 5 on the heap
println!("b = {}", *b); // dereferences with * to read the value
}أنواع العودية وقوائم السلبيات
النوع العودي هو نوع يمكن أن يحتوي على قيمة خاصة به كجزء من تعريفه. يجب أن يعرف الصدأ الحجم الدقيق للنوع في وقت الترجمة. نظرًا لأن عمق النوع العودي يمكن أن يكون لا نهائيًا، فإن المترجم يُنشئ خطأ ما لم تستخدم صندوقًا (له حجم ثابت، لأنه مؤشر):
enum List {
Cons(i32, Box<List>),
Nil,
}Rc<T>: العد المرجعي
هناك حالات حيث يمكن أن يكون لقيمة واحدة مالكين متعددين. يقوم المؤشر الذكي Rc<T> (تم حساب المرجع) بتتبع عدد المراجع إلى القيمة لتحديد ما إذا كانت القيمة لا تزال قيد الاستخدام. إذا انخفض العدد إلى الصفر، فسيتم إلغاء تخصيص المورد بأمان.
[!ملاحظة]
Rc<T>مناسب فقط للسيناريوهات ذات الترابط الواحد. بالنسبة للسيناريوهات متعددة الخيوط (المتزامنة)، يجب عليك استخدامArc<T>(تم حساب المرجع الذري).
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: تخصيص الكومة مع الصندوق
قم بتخصيص قيمة عددية 42 على الكومة باستخدام Box::new وقم بتعيينها لمتغير يسمى val. بعد ذلك، قم بطباعة القيمة المخزنة في Box عن طريق إلغاء الإشارة إليها بشكل صريح باستخدام عامل التشغيل *.
إظهار التلميح
استخدم `let val = Box::new(42);` لتخزين القيمة في الكومة وإلغاء الإشارة إليها باستخدام `*val` داخل `println!`.
الحل متاح بعد 3 من المحاولات
التمرين 2: بنية البيانات العودية (قائمة السلبيات)
حدد تعدادًا متكررًا يسمى 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
استخدم std::rc::Rc لإنشاء عدد صحيح مشترك يحتوي على القيمة 100 في المتغير a. انسخ المرجع إلى متغير b باستخدام Rc::clone. وأخيرًا، قم بطباعة عدد المراجع النشطة باستخدام الدالة Rc::strong_count.
إظهار التلميح
قم بإنشاء القيمة باستخدام `Rc::new(100)`. استنسخ باستخدام `Rc::clone(&a)`واعرض عدد المالك باستخدام`Rc::strong_count(&a)`.
الحل متاح بعد 3 من المحاولات