محتويات الدورة

Rust / المتقدم / الدرس 16

افتراضات unsafe وحدود الربط الخارجي

اشرح التزامات unsafe على المبرمج وفضّل التجريد الآمن وراجع الاستدعاءات الخارجية كعقود ملكية وتوافق ثنائي صريحة.

وقت القراءة التقريبي: 4 دقيقةتاريخ مراجعة المحتوى
في هذه الصفحة

أهداف الدرس

  • تمييز عمليات unsafe من سياسة التطبيق غير المفحوصة.
  • ذكر شروط الصلاحية والمحاذاة والتشارك والعمر.
  • وصف كتل unsafe extern في إصدار 2024 دون تنفيذ شيفرة خارجية.

تنقل unsafe التزامات الإثبات

تتيح unsafe عمليات لا يتحقق المترجم منها كاملًا كفك المؤشر الخام واستدعاء دالة unsafe. لا تعطل فحص الأنواع ولا تبرر السلوك غير المعرّف أو تصحح ذاكرة فاسدة. يجب أن يصح التجريد الآمن لكل مستدعٍ تسمح به واجهته. اذكر الخاصية قبل الكتلة وصغّر التنفيذ الموثوق. اشرح في Safety سبب تحقق الشروط هنا لا مجرد خطورة العملية. وتبقى الأخطاء المنطقية وحدود الموارد محتاجة فحصًا منفصلًا.

استخدام مكوّن آمن مثبت عند الإمكان

لا يحتاج تعديل جزأين إلى مؤشرات يدوية إذا أثبتت واجهة آمنة عدم التداخل. تعيد split_at_mut شريحتين منفصلتين بعد فحص الحد. يعدّل المثال عنصرًا في كل جانب ثم يطبع المصفوفة باستعارة مشتركة لاحقة، بعد انتهاء آخر استخدام للحصريتين. قد تستخدم المكتبة unsafe داخليًا ولواجهتها عقد مضبوط. اعتمد العقد بدل نسخ التنفيذ المنخفض دون فهم كل شروطه.

rust تعديل منفصل بواجهة آمنة
fn main() {
    let mut values = [1u32, 2, 3, 4];
    let (left, right) = values.split_at_mut(2);
    left[0] += 10;
    right[0] += 20;
    println!("{:?}", values);
}
الناتج المتوقع
[11, 2, 23, 4]

يثبت التقسيم عدم التداخل، ويتغير العنصر الأول من كل منطقة فقط.

فحص التمثيل قبل عبور الحد

تتوقع واجهات C غالبًا بايتات منتهية بصفر بدل str. تملك CString بايتات مفحوصة بلا صفر داخلي وتضيف النهاية، وتستعير CStr التمثيل المناسب. لا تضمنان UTF-8 لكل بايتات أجنبية. يبني المثال CString ويفحص أطوالها دون استدعاء خارجي. يبقى مؤشر as_ptr مستعارًا فلا تحرره الشيفرة الخارجية أو تحتفظ به بعد العمر الموثق. تفقد إعادة مؤشر من CString مؤقتة مالكه فورًا.

rust سلسلة على نمط C مفحوصة دون تنفيذ خارجي
use std::ffi::CString;
fn main() {
    let name = CString::new("lab").expect("literal has no interior nul");
    println!("{} {}", name.as_bytes().len(), name.as_bytes_with_nul().len());
    println!("{}", CString::new(b"ab".to_vec()).is_err());
}
الناتج المتوقع
3 4
true

لـlab ثلاثة بايتات وأربعة مع النهاية، ويُرفض الإدخال ذو الصفر الداخلي.

التصريح الخارجي ادعاء عن نظام آخر

يشترط إصدار 2024 كتل unsafe extern للتصريحات الخارجية ومسؤولية صحة التوقيع. لا يجعل توافق C أنواع String وVec وتعداد Rust العادي مماثلة لبنى C. استخدم أنواعًا متوافقة وrepr(C) حيث يلزم وافحص الحشو والتهيئة والملكية والأخطاء منفصلة. قد يترجم تصريح خاطئ للمكتبة المرتبطة. المقطع تابع وغير قابل للتشغيل لغياب تنفيذ المورد وعقد أمانه، ولا تُختلق له نتيجة.

rust تصريح أو مثال مرفوض عمدًا
unsafe extern "C" {
    fn vendor_count(data: *const u8, length: usize) -> i32;
}

تصريح اعتماد فقط؛ افحص ترويسة المورد والتوافق وصلاحية المؤشر والاحتفاظ به والأخطاء قبل أي استدعاء.

مراجعة حد unsafe كاملًا

أثبت للخام الحدود والمحاذاة والتهيئة والصلاحية والتشارك والعمر حتى آخر وصول. حدّد في الربط من يخصص ويحرر وخيوط الردود وعبور الاستثناء أو panic للواجهة الثنائية، ولا تفك المكدس عبر واجهة تمنعه. افحص المدخلات بغلاف آمن قبل الاستدعاء الضيق. توفر Miri والمطهّرات أدلة لمسارات مدعومة ولا تصدقان مكتبة أجنبية اعتباطية. تنفذ الدروس البدائل الآمنة فقط ويحتاج تصميم تجريد unsafe إلى مراجعة متخصصة.

الأخطاء الشائعة وإصلاحها

وصف unsafe بأنها تعطل كل فحوص المترجم.

لماذا يحدث؟
خُلطت صلاحيات محددة بتنفيذ مطلق.
كيف تصلحه؟
احصر العملية وشروط أمانها الدقيقة.
كيف تتجنب تكراره؟
ضيّق كتل unsafe وبررها.

خروج مؤشر CString بعد إسقاط مالكه.

لماذا يحدث؟
أُهمل عمر المؤشر المستعار عند الحد الخارجي.
كيف تصلحه؟
احتفظ بالمالك طوال الاستخدام الخارجي الموثق.
كيف تتجنب تكراره؟
وثّق الاحتفاظ ومسؤوليات التحرير.

افتراض توافق بنية Rust ثنائيًا دون مراجعة.

لماذا يحدث؟
خُلط التشابه المصدري بتوافق التخطيط.
كيف تصلحه؟
استخدم تمثيلات مثبتة وتصريحات خارجية مطابقة.
كيف تتجنب تكراره؟
افحص واجهة الهدف وترويسات المكتبة معًا.

اختبر فهمك

1. لماذا تفضّل split_at_mut على بناء شريحتين خامتين؟ سمّ إثباتها وشرطًا يبقى على المستدعي.

اعرض الحل المشروح

تثبت الواجهة منطقتين منفصلتين وتعالج المؤشرات والتشارك داخليًا. يبقى اختيار حد صالح أو قبول panic الموثقة وفهرسة كل جزء ضمن حدوده. يقسم الاثنان أربعة عناصر إلى زوجين فيصلح العنصر صفر في كل جانب.

2. تخزن دالة C مؤشر نص لردود لاحقة. فسّر عدم كفاية CString مؤقتة وما يجب أن يحدده الغلاف.

اعرض الحل المشروح

قد تُسقط المؤقتة بعد الاستدعاء بينما يحتفظ C بالمؤشر. يجب إبقاء التخزين حتى تؤكد المكتبة انتهاء كل استخدام، بما فيه الرد والإغلاق، مع تحديد وصول الخيوط وبروتوكول التحرير. لا يمدد نسخ المؤشر الخام العمر.

أهم ما تعلمته

  • تحتاج unsafe إثباتات لا معايير أمان أضعف.
  • فضّل مكونات آمنة بعقود صريحة.
  • يجمع الربط التزامات التوافق والملكية والعمر والفشل.

المراجع الرسمية