لماذا توجد Rust "غير آمنة" أصلًا؟
المترجم متحفّظ بطبعه: يرفض أحيانًا كودًا صحيحًا فعليًا لأنه لا يستطيع إثبات
أمانه رياضيًا. وunsafe تعطيك مخرجًا موثّقًا لهذه الحالات، بالإضافة إلى حاجات
حتمية مثل التعامل المباشر مع الذاكرة أو استدعاء كود بلغة C.
القدرات الخمس
كتلة unsafe { ... } تسمح بخمسة أشياء فقط، ممنوعة في Rust الآمنة:
- فكّ مؤشّر خام (
*const Tأو*mut T). - استدعاء دالة أو تابع
unsafe. - قراءة/تعديل متغيّر ثابت قابل للتغيير (
static mut). - تنفيذ سمة
unsafe trait. - الوصول لحقول
union.
fn main() {
let mut num = 5;
let r1 = &raw const num; // إنشاء مؤشّر خام: آمن
let r2 = &raw mut num;
unsafe {
println!("{}", *r1); // فكّه: يحتاج unsafe
*r2 += 1;
}
}
unsafe لا تُعطّل فاحص الاستعارة
مهم جدًا: داخل unsafe، قواعد الاستعارة والأنواع ما زالت تُفحص. unsafe
تفتح فقط الأبواب الخمسة أعلاه، ولا شيء غيرها.
unsafe fn dangerous() {
// كود يفترض شروطًا لا يستطيع المترجم التحقّق منها بنفسه
}
fn main() {
unsafe {
dangerous(); // استدعاؤها إلزامي داخل كتلة unsafe
}
}
تغليف unsafe بواجهة آمنة
النمط الشائع: اكتب الجزء غير الآمن داخل دالة صغيرة، واجعل توقيعها آمنًا حتى
لا ينتشر unsafe في باقي المشروع:
fn first_byte(bytes: &[u8]) -> u8 {
assert!(!bytes.is_empty());
let ptr = bytes.as_ptr();
unsafe { *ptr } // مضمون الأمان بفضل assert! أعلاه
}
المستخدم يستدعي first_byte كأي دالة آمنة عادية، دون أن يكتب unsafe بنفسه.
💡 القاعدة الذهبية: اجعل كتل
unsafeصغيرة قدر الإمكان ووثّق سبب أمانها بتعليق// SAFETY: ...— يسهّل تتبّع الأخطاء لاحقًا. ولا تلجأ لـunsafeإلا إذا تأكّدت أن Rust الآمنة لا تكفي فعليًا.
🎯 التالي: البرمجة غير المتزامنة (async/await).