معالجة الأخطاء في Rust: try/catch وما بعدها

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة
```html

مقدمة: فلسفة معالجة الأخطاء في 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 ```

المدونات

توصيات الكتب