Rust / المتوسط / الدرس 7
Result والأخطاء والاستعادة
مثّل الفشل المتوقع في أنواع الإرجاع وانشر السياق بوعي وميّز أخطاء المجال القابلة للاستعادة من panic المرتبطة بخرق الافتراضات.
في هذه الصفحة
أهداف الدرس
- استخدام Result والمطابقة ومعامل علامة الاستفهام.
- إبقاء أخطاء التحليل والمجال قابلة للتمييز.
- اختيار حد للاستعادة وتجنب unwrap في الحالات المعتادة.
قيمة الفشل جزء من العقد
تملك Result<T, E> البديلين Ok(T) وErr(E) للنجاح والفشل الصريحين. يعالج المستدعي النتيجة أو ينشرها قبل اعتبارها قيمة ناجحة. تعبّر Option عن الغياب دون سببه، فاختر Result حين يهم التفريق. لا يسجل إرجاع الخطأ ولا يعيد المحاولة ولا ينهي العملية تلقائيًا؛ فهذه سياسة حد يملك سياقًا كافيًا. حدّد حالات الإدخال والسجلات والإدخال والإخراج المتوقعة بدل رسالة فشل موحدة لكل شيء.
إعطاء الأخطاء فئات ثابتة مفيدة
يحوّل المحلل الأول النص كاملًا إلى u32 ويحوّل فشل التحويل إلى تعداد مجال، ثم يفحص الحد الأقصى منفصلًا. يتيح التعداد استجابات مختلفة دون فحص الرسالة. فُحص تنسيق Debug المعروض بالأدوات المعلنة؛ ولا ينبغي استخدام Debug كتنسيق تسلسل ثابت في البروتوكولات العامة. تستطيع المكتبة العامة تطبيق Display وError للرسائل وسلاسل الأسباب مع حفظ الفئة للمعالجة البرمجية. تجنب تخزين مرجع لإدخال مؤقت داخل خطأ يغادر نطاقه.
#[derive(Debug)]
enum CountError { InvalidInteger, TooLarge }
fn count(text: &str) -> Result<u32, CountError> {
let value = text.parse::<u32>().map_err(|_| CountError::InvalidInteger)?;
if value > 100 { return Err(CountError::TooLarge); }
Ok(value)
}
fn main() {
println!("{:?}", count("12"));
println!("{:?}", count("101"));
println!("{:?}", count("x"));
}
Ok(12)
Err(TooLarge)
Err(InvalidInteger)
تنجح اثنا عشر وتخالف مئة وواحد المجال ويتعذر تحليل x كعدد.
علامة الاستفهام تنشر ولا تستعيد
تستخرج ? قيمة Ok أو تعود مبكرًا بخطأ محول مناسب في دالة تعيد Result. لا تبتلع الخطأ ولا تعيد المحاولة. يستخدم المثال عددين مفحوصين وجمعًا مفحوصًا مع فئة منفصلة للفيض. أضف السياق حيث تعرف العملية المقصودة ثم اترك قرار الاستجابة للحد المختار. لا تغلف الخطأ برسائل متطابقة في كل طبقة، واحتفظ بالمصدر حين يفيد ويكون تخزينه آمنًا.
fn parse(text: &str) -> Result<u32, &'static str> {
text.parse::<u32>().map_err(|_| "invalid count")
}
fn combine(left: &str, right: &str) -> Result<u32, &'static str> {
let left = parse(left)?;
let right = parse(right)?;
left.checked_add(right).ok_or("sum overflow")
}
fn main() {
println!("{:?}", combine("7", "8"));
println!("{:?}", combine("7", "bad"));
}
Ok(15)
Err("invalid count")
مجموع الزوج الناجح خمسة عشر، ويعيد الإدخال الثاني الفاسد خطأ قبل محاولة الجمع.
panic ليست نتيجة تحقق عادية
تسبب unwrap وexpect حالة panic عند البديل غير المتوقع. قد توثقان افتراضًا محليًا مثبتًا، لكن أخطاء المستخدم المعتادة ينبغي أن تكون قيم فشل. قد تفك panic المكدس أو توقف العملية وفق الإعداد؛ فلا تضمن عمل كل مدمّر مع كل سياسة. ليست catch_unwind معالجة عامة ولا تلتقط التوقف المباشر. اختر استعادة العملية أو المهمة صراحة؛ فالأمان لا يمنع تعطيل الخدمة عبر panic أو نفاد الموارد.
اختبار الأخطاء كانتقالات حالة
حدّد لكل عملية هل يبقي الفشل الحالة ثابتة أو محدثة جزئيًا أو قابلة لاستعادة موثقة. يمنع التحليل إلى مرشحات محلية قبل الالتزام تحديثات جزئية غالبًا. افحص فئة الخطأ والحالة اللاحقة مع النص المشوه والحدود والفيض. أعد المحاولة فقط إذا أمكن تغير السبب وبحد معلوم. لا تسجل أسرارًا أو إدخالًا كاملًا لمجرد السياق. تخدم الرسائل البشرية والفئات الثابتة حاجتين مختلفتين فصممهما معًا.
الأخطاء الشائعة وإصلاحها
إدخال مستخدم فاسد ينهي البرنامج.
- لماذا يحدث؟
- استُخدمت unwrap لخطأ Result متوقع.
- كيف تصلحه؟
- طابق الخطأ أو انشره إلى حد الاستعادة.
- كيف تتجنب تكراره؟
- اقصر panic على افتراضات مكسورة موثقة.
توقع تسجيل الفشل من علامة الاستفهام.
- لماذا يحدث؟
- خُلط نشر الخطأ بمعالجته.
- كيف تصلحه؟
- عالج أو سجل مرة عند الحد المختار.
- كيف تتجنب تكراره؟
- أسند مسؤولية الاستعادة إلى طبقة محددة.
مستدعون يحللون رسائل الخطأ لاختيار السلوك.
- لماذا يحدث؟
- لا تعرض الواجهة فئات أخطاء ثابتة.
- كيف تصلحه؟
- أعد تعدادًا أو نوع خطأ منظمًا.
- كيف تتجنب تكراره؟
- صمّم الفئات البرمجية منفصلة عن الصياغة.
اختبر فهمك
1. أضف اختبار جمع أقصى u32 مع واحد، واذكر النتيجة وسبب استقلالها عن فحص الفيض في بناء الإصدار.
اعرض الحل المشروح
قدّم "4294967295" و"1". ينجح التحليل وتعطي checked_add القيمة None فتنتج combine القيمة Err("sum overflow"). السياسة صريحة فيتفق بناء التصحيح والإصدار. قارن قيمة الخطأ في الاختبار بدل توقع panic.
2. يفحص تحديث إعداد حقلين ويغير الأول قبل تحليل الثاني. اقترح سياسة تحفظ الإعداد القديم عند الفشل.
اعرض الحل المشروح
حلل الحقلين وافحصهما كمرشحين محليين أولًا ثم استبدل الإعداد بمرحلة التزام مقصودة بعد نجاحهما. أعد خطأ منظمًا عند فشل أحدهما. افحص بقاء الحقلين السابقين عند فساد الثاني؛ فاختبار الخطأ وحده لا يكشف تعديلًا جزئيًا.
أهم ما تعلمته
- استخدم أخطاء منظمة للفشل المتوقع.
- النشر والاستعادة قراران مختلفان.
- اختبر حالة الفشل بدقة قيم النجاح نفسها.