عامل الاستفهام ? في Rust: ما هو ولماذا تحتاجه كمبتدئ

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

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

ابدأ الدورة

ما هو عامل الاستفهام في Rust؟ شرح للمبتدئين

\n\n

إذا كنت قد بدأت للتو في تعلم Rust، فمن المحتمل أنك صادفت كودًا ينتهي برمز غريب ?. يبدو الأمر هكذا تقريبًا:

\n\n
fn read_file(path: &str) -> Result<String, io::Error> {\n    let mut file = File::open(path)?;\n    let mut contents = String::new();\n    file.read_to_string(&mut contents)?;\n    Ok(contents)\n}
\n\n

أو هكذا:

\n\n
fn get_user_name(user_id: u32) -> Option<String> {\n    let user = find_user(user_id)?;\n    Some(user.name)\n}
\n\n

هذا الرمز الصغير هو عامل الاستفهام (question mark operator). بالنسبة للمبتدئ، يبدو كالسحر: يصبح الكود أقصر وأنظف، وتختفي الأخطاء في مكان ما. ولكن ماذا يحدث حقًا وراء هذا الرمز؟ دعنا نفهم الأمر مرة واحدة وإلى الأبد.

\n\n

بالمناسبة، هذا السؤال شائع جدًا لدرجة أنه حصل على آلاف المشاهدات على Stack Overflow. ليس من المستغرب — فالعامل ? يغير طريقة كتابتك للكود في Rust.

\n\n

لماذا نحتاج عامل الاستفهام أساسًا؟

\n\n

تشتهر Rust بنظام الأنواع الخاص بها والأمان. لا توجد استثناءات (exceptions) كما في Python أو Java. بدلاً من ذلك، الأخطاء هي مجرد قيم. لهذا الغرض، يوجد نوعان خاصان:

\n\n
    \n
  • Result<T, E> — العملية قد تنجح (Ok(T)) أو تفشل مع خطأ (Err(E)).
  • \n
  • Option<T> — القيمة قد تكون موجودة (Some(T)) أو غائبة (None).
  • \n
\n\n

المشكلة هي أن معالجة هذه الأنواع يدويًا أمر مرهق. تخيل أنك بحاجة لفتح ملف، قراءته، تحليل JSON، وإرساله عبر الشبكة. كل خطوة قد تعيد خطأ. بدون ?، ستكتب أكوادًا ضخمة باستخدام match أو unwrap.

\n\n

عامل الاستفهام يحل هذه المشكلة بأناقة.

\n\n

كيف يعمل عامل ?: شرح بسيط

\n\n

تخيل أن العامل ? هو مساعد ذكي يقول:

\n\n
    \n
  • إذا كان كل شيء على ما يرام — أعطني القيمة، وسأكمل العمل.
  • \n
  • إذا حدث خطأ ما — سأعيد الخطأ فورًا من الدالة الحالية.
  • \n
\n\n

لنلقِ نظرة على مثال محدد. بدون عامل الاستفهام:

\n\n
use std::fs::File;\nuse std::io::{self, Read};\n\nfn read_file(path: &str) -> Result<String, io::Error> {\n    let file_result = File::open(path);\n    \n    let mut file = match file_result {\n        Ok(f) => f,\n        Err(e) => return Err(e),\n    };\n    \n    let mut contents = String::new();\n    let read_result = file.read_to_string(&mut contents);\n    \n    match read_result {\n        Ok(_) => Ok(contents),\n        Err(e) => return Err(e),\n    }\n}
\n\n

والآن مع عامل الاستفهام:

\n\n
use std::fs::File;\nuse std::io::{self, Read};\n\nfn read_file(path: &str) -> Result<String, io::Error> {\n    let mut file = File::open(path)?;\n    let mut contents = String::new();\n    file.read_to_string(&mut contents)?;\n    Ok(contents)\n}
\n\n

الفرق هائل. بدلاً من 15 سطرًا — 5 أسطر فقط. وهذا ليس مجرد سكر نحوي. العامل ? يجعل الكود أكثر قابلية للقراءة وأقل عرضة للأخطاء.

\n\n

غوص عميق: ماذا يحدث تحت الغطاء

\n\n

عندما تكتب File::open(path)?، يقوم مترجم Rust بتوسيع هذا إلى كود مشابه لما يلي:

\n\n
match File::open(path) {\n    Ok(val) => val,\n    Err(err) => return Err(From::from(err)),\n}
\n\n

لاحظ From::from(err). هذه نقطة أساسية. العامل ? لا يعيد الخطأ فحسب — بل يحول نوع الخطأ تلقائيًا باستخدام trait From.

\n\n

ماذا يعني هذا عمليًا؟ لنفترض أنك

المدونات

توصيات الكتب