مقدمة: فلسفة معالجة الأخطاء في Rust
على عكس العديد من اللغات التي تستخدم الاستثناءات (Java, Python, C++)، لا تستخدم Rust البنية التقليدية try/catch. بدلاً من ذلك، تقدم اللغة نهجًا أكثر وضوحًا وأمانًا من حيث النوع. الأخطاء في Rust ليست تدفقات تحكم مخفية، بل هي قيم مرتجعة. وهذا يجعل الكود قابلاً للتنبؤ ويحمي من الأعطال "الصامتة".
يوجد في Rust نوعان رئيسيان لتمثيل الحالات الخاطئة:
- Result<T, E> — للأخطاء القابلة للاسترداد (أخطاء الإدخال/الإخراج، التحليل، منطق الأعمال).
- Option<T> — لغياب القيمة (مشابه لـ null، لكنه آمن).
في هذه المقالة، سنشرح كيفية العمل مع هذه الأنواع، وما الذي يحل محلها من عالم try/catch، وكيفية كتابة كود موثوق به في Rust.
1. Result<T, E>: الأداة الرئيسية لمعالجة الأخطاء
النوع Result هو enum له خياران: Ok(T) (نجاح) و Err(E) (خطأ). يُستخدم في معظم الدوال التي قد تفشل.
1.1. إنشاء وإرجاع Result
use std::fs::File;use std::io::Read;
fn read_file(path: &str) -> Result<String, std::io::Error> { let mut file = File::open(path)?; // العامل ? — مشابه لـ try! let mut contents = String::new(); file.read_to_string(&mut contents)?; Ok(contents)}هنا، العامل ? هو عنصر أساسي. يعمل مثل try في اللغات الأخرى: إذا كانت النتيجة Err، تقوم الدالة فورًا بإرجاع هذا الخطأ. إذا كانت Ok، يتم استخراج القيمة.
1.2. المعالجة باستخدام match
الطريقة الأكثر وضوحًا هي استخدام match:
let result = read_file("config.txt");
match result { Ok(content) => println!("تمت قراءة الملف: {}", content), Err(e) => eprintln!("خطأ: {}", e),}هذا مشابه لـ try/catch، لكنه أكثر مرونة وتحكمًا.
1.3. الطرق المساعدة
.unwrap()— يسبب ذعرًا (panic) عند الخطأ (فقط للنماذج الأولية)..expect(msg)— يسبب ذعرًا مع رسالة مخصصة..unwrap_or(default)— يُرجع قيمة افتراضية..is_ok()/.is_err()— التحقق من الحالة.
2. Option<T>: العمل الآمن مع غياب القيمة
Option هو enum آخر بخيارين Some(T) و None. يحل محل null و nullptr، مما يستبعد أخطاء إلغاء الإشارة للمؤشر الفارغ.
2.1. مثال على الاستخدام
fn find_user(id: u32) -> Option<String> { if id == 1 { Some("Alice".to_string()) } else { None }}
let user = find_user(2);println!("المستخدم: {}", user.unwrap_or("ضيف".to_string()));2.2. الدمج مع Result
غالبًا ما يتم تحويل Option إلى Result للمعالجة الموحدة:
fn get_user_name(id: u32) -> Result<String, String> { find_user(id).ok_or("المستخدم غير موجود".to_string())}الطريقة .ok_or() تحول Some إلى Ok، و None إلى Err.
3. العامل ?: بديل أنيق لـ try/catch
العامل ? هو سكر نحوي (syntactic sugar) يقوم تلقائيًا بفك تغليف Result أو Option. وهو متاح فقط في الدوال التي تُرجع النوع المناسب.
3.1. مثال مع سلسلة استدعاءات
use std::num::ParseIntError;
fn parse_and_add(a: &str, b: &str) -> Result<i32, ParseIntError> { let x: i32 = a.parse()?; let y: i32 = b.parse()?; Ok(x
```