عامل علامة الاستفهام '?'
جزء من قسم Logic & Flow في رحلة Rust على Coddy — الدرس 46 من 66.
عند العمل مع الدوال التي تُرجع Result، غالباً ما تحتاج إلى معالجة الأخطاء إما عن طريق التعامل معها محلياً أو تمريرها إلى الدالة المستدعية. توفر لغة Rust عاملاً أنيقاً لهذا النمط الشائع: عامل علامة الاستفهام ?.
يوفر المشغل ? طريقة نظيفة لنشر الأخطاء دون كتابة عبارات match مطولة. عندما تضع ? بعد Result، فإنه يقوم تلقائيًا بفك قيمة Ok أو يعود فورًا من الدالة الحالية مع Err:
fn parse_and_double(s: &str) -> Result<i32, std::num::ParseIntError> {
let number = s.parse::<i32>()?; // إذا فشل التحليل، قم بإرجاع الخطأ
Ok(number * 2) // إذا نجح، ضاعف الرقم
}هذا يعادل كتابة تعبير match كامل، ولكنه أكثر إيجازاً بكثير. إذا أرجع s.parse::<i32>() القيمة Ok، فإن ? يستخرج القيمة ويسندها إلى number. أما إذا أرجع Err، فإن ? يعيد ذلك الخطأ فوراً من الدالة بأكملها.
يمكن استخدام عامل الـ ? فقط في الدوال التي تعيد Result (أو Option)، لأنه يحتاج إلى أن يكون قادرًا على إرجاع خطأ.
التحدي
سهلسوف تتلقى مدخلين. المدخل الأول هو سلسلة نصية يجب تحويلها إلى عدد صحيح، والمدخل الثاني هو سلسلة نصية أخرى يجب تحويلها أيضاً إلى عدد صحيح. قم بإنشاء دالة تسمى add_numbers تأخذ شريحتين نصيتين (string slices) كمعاملات وتعيد Result<i32, std::num::ParseIntError>. داخل الدالة، استخدم عامل التشغيل ? لتحويل كلا السلسلتين وإرجاع مجموعهما ملفوفاً في Ok. قم باستدعاء الدالة مع المدخلين ومعالجة النتيجة باستخدام match.
المتطلبات:
- قراءة المدخل الأول (الرقم الأول كسلسلة نصية) وإزالة المسافات البيضاء
- قراءة المدخل الثاني (الرقم الثاني كسلسلة نصية) وإزالة المسافات البيضاء
- إنشاء دالة
add_numbersتأخذ معاملين من نوع&strوتعيدResult<i32, std::num::ParseIntError> - داخل الدالة، قم بتحويل السلسلة الأولى إلى
i32باستخدام عامل التشغيل? - قم بتحويل السلسلة الثانية إلى
i32باستخدام عامل التشغيل? - أعد
Ok(sum)حيث sum هو ناتج جمع كلا الرقمين المحولين - استدعِ الدالة مع سلسلتي المدخلات
- استخدم تعبير
matchلمعالجةResult - في ذراع
Ok(value)، اطبع:Sum: [value] - في ذراع
Err(_)، اطبع:Parsing error
المدخلات:
- السطر الأول: سلسلة نصية تمثل رقماً (مثلاً،
15) - السطر الثاني: سلسلة نصية تمثل رقماً آخر (مثلاً،
27)
المخرجات:
- إذا كانت كلتا السلسلتين أرقاماً صالحة:
Sum: [result] - إذا تعذر تحويل أي من السلسلتين:
Parsing error
ورقة مرجعية
يوفر عامل علامة الاستفهام ? طريقة موجزة لنشر الأخطاء في الدوال التي تعيد Result.
عند وضعه بعد Result، يقوم عامل ? بـ:
- فك قيمة
Okومواصلة التنفيذ - إرجاع
Errفوراً من الدالة الحالية في حال حدوثه
fn parse_and_double(s: &str) -> Result<i32, std::num::ParseIntError> {
let number = s.parse::<i32>()?; // إذا فشل التحليل، يتم إرجاع الخطأ
Ok(number * 2) // إذا نجح، يتم مضاعفة الرقم
}يمكن استخدام عامل ? فقط في الدوال التي تعيد نوع Result أو Option.
جرّب بنفسك
use std::io;
// TODO: أنشئ دالة add_numbers هنا
// يجب أن تأخذ معاملين من نوع &str وتعيد Result<i32, std::num::ParseIntError>
fn main() {
// قراءة المدخل الأول
let mut input1 = String::new();
io::stdin().read_line(&mut input1).expect("Failed to read line");
let input1 = input1.trim();
// قراءة المدخل الثاني
let mut input2 = String::new();
io::stdin().read_line(&mut input2).expect("Failed to read line");
let input2 = input2.trim();
// TODO: استدعِ add_numbers مع input1 و input2
// TODO: استخدم match للتعامل مع Result
// اطبع "Sum: [value]" في حالة Ok(value)
// اطبع "Parsing error" في حالة Err(_)
}يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Logic & Flow
1تدفق التحكم المتقدم
تعبير 'match'مطابقة قيم متعددةمطابقة النطاقاتتعبير 'if let'الحلقات التكرارية كتعبيراتمراجعة - محلل أوامر بسيط4تجميع البيانات باستخدام Structs
ما هو الـ Struct؟نظرة عامة على الـ Structsالوصول إلى حقول الـ Structالـ Structs القابلة للتعديلالـ Structs كمعاملات للدوالالـ Tuple Structsمراجعة - إنشاء Book Struct7معالجة الأخطاء باستخدام 'Result'
ما هو 'Result'؟استخدام 'match' مع 'Result'is_ok() و is_err()اختصارات: unwrap و expectعامل علامة الاستفهام '?'تحويل السلاسل النصية إلى أرقامملخص - دالة القسمة الآمنة10الـ Closures والدوال المجهولة
ما هو الـ Closure؟تعريف Closure بسيطالـ Closures مع المعاملاتالتقاط البيئة المحيطةملخص - Closure الجامع البسيط2مقدمة في الـ Vectors
ما هو الـ Vector؟إنشاء Vectorإضافة عناصر باستخدام pushالوصول إلى عناصر الـ Vectorالتكرار عبر الـ Vectorالتكرار القابل للتعديل (Mutable Iteration)إزالة العناصرمراجعة - متتبع نقاط بسيط5أزواج (المفتاح والقيمة) مع Hash Maps
ما هو Hash Map؟إنشاء Hash Mapإدراج أزواج (المفتاح والقيمة)الوصول إلى القيمالتكرار عبر Hash Mapتحديث قيمةإزالة زوجملخص - عداد الكلمات6التعامل مع غياب القيم باستخدام 'Option'
ما هو الـ 'Option'؟استخدام 'match' مع 'Option''is_some()' و 'is_none()'فك تغليف الـ 'Option'الدالة 'expect'توفير قيمة افتراضية: 'unwrap_or'مراجعة - البحث عن عنصر9شرائح الـ String والمزيد
String مقابل &strإنشاء شرائح الـ Stringالشرائح كمعاملات للدوالشرائح أخرىمراجعة - إيجاد الكلمة الأولى