معالجة الأخطاء المتينة باستخدام `Result`
طبّقوا معالجة الأخطاء وفق أسلوب Rust باستخدام تعداد `Result` والعامل `?` وأنواع الأخطاء المخصّصة لبناء تطبيقات متينة.
معالجة الأخطاء المتينة باستخدام `Result` درس مجاني في Learn Rust Coding على CoddyKit. هذا هو الدرس 3 من أصل 3. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Learn Rust Coding، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Learn Rust Coding 3 دروس في المجموع.
معالجة الأخطاء في Rust
الأخطاء جزء من البرمجة. في Rust، نميز بين نوعين رئيسيين: الأخطاء القابلة للاسترداد والأخطاء غير القابلة للاسترداد.
- تشير الأخطاء غير القابلة للاسترداد عادةً إلى وجود أخطاء برمجية، وتتسبب في توقف البرنامج باستخدام
panic!. - تعني الأخطاء القابلة للاسترداد حدوث مشكلة يمكن التعامل معها، مثل عدم العثور على ملف.
يساعدكم Rust على التعامل مع الأخطاء القابلة للاسترداد بطريقة سلسة.
`panic!` مقابل `Result`
عند حدوث خطأ غير قابل للاسترداد، يستدعي Rust الماكرو panic!. يفك هذا الماكرو مكدس الاستدعاءات وينهي البرنامج، وعادةً ما يطبع رسالة خطأ.
أما بالنسبة إلى الأخطاء القابلة للاسترداد، فيستخدم Rust تعداد Result. يخبركم هذا التعداد بما إذا نجحت العملية (Ok) أو فشلت (Err)، مما يتيح لبرنامجكم تحديد الإجراء التالي.
تعداد `Result`
يُعرّف تعداد Result مفاهيميًا كما يلي:
enum Result<T, E> {
Ok(T),
Err(E),
}
- يمثل
Tنوع القيمة التي تُعاد عند النجاح. - يمثل
Eنوع الخطأ الذي يُعاد عند الفشل.
إنه أسلوب قوي للتصريح صراحةً بأن الدالة قد تفشل.
المطابقة على `Result`
الطريقة الأكثر شيوعًا للتعامل مع Result هي استخدام تعبير match. يتيح لكم ذلك تنفيذ كتل كود مختلفة استنادًا إلى ما إذا كانت قيمة Result هي Ok أم Err.
حاولوا تشغيل هذا المثال الذي يحاول تحليل رقم:
fn parse_and_add(text: &str) -> Result<i32, std::num::ParseIntError> {
text.parse::<i32>()
}
fn main() {
let num_str = "123";
let result = parse_and_add(num_str);
match result {
Ok(num) => println!("Parsed number: {}", num),
Err(e) => println!("Error parsing: {}", e),
}
let bad_str = "abc";
let bad_result = parse_and_add(bad_str);
match bad_result {
Ok(num) => println!("Parsed number: {}", num),
Err(e) => println!("Error parsing: {}", e),
}
}استخراج قيم Result (بحذر!)
قد تكونون متأكدين أحيانًا من أن عملية ما لن تفشل، أو قد ترغبون في أن يتعطل برنامجكم إذا فشلت. تستخرج أساليب مثل unwrap() وexpect() قيمة Ok.
unwrap(): تعيد قيمةOkأو تتسبب في حدوث panic إذا كانت القيمةErr.expect("message"): تؤدي وظيفةunwrap()نفسها، لكنها تتيح لكم تقديم رسالة مخصصة عند حدوث panic.
استخدموا هذه الأساليب باعتدال، ولا سيما في الاختبارات أو عندما يكون الفشل غير قابل للاسترداد فعلًا.
التعريف بمعامل `?`
قد تصبح كتابة عبارات match لكل قيمة Result أمرًا متكررًا. يوفر المعامل ? اختصارًا موجزًا لتمرير الأخطاء.
عند وضع ? بعد تعبير يعيد قيمة Result، فإنه ينفذ أمرين:
- إذا كانت قيمة
ResultهيErr، فإنه يعيدErrفورًا من الدالة الحالية. - إذا كانت قيمة
ResultهيOk، فإنه يستخرج قيمةOkويتابع التنفيذ.
تطبيق معامل `?` عمليًا
يجعل المعامل ? كتابة الدوال التي قد تفشل أكثر ترتيبًا بكثير. تذكروا أن الدالة التي تستخدم ? يجب أن تعيد هي نفسها قيمة Result (أو Option).
لاحظوا كيف يبسّط ? معالجة الأخطاء في مثال قراءة الملف هذا:
use std::fs::File;
use std::io::{self, Read};
fn read_username_from_file() -> Result<String, io::Error> {
let mut f = File::open("hello.txt")?; // Propagates error if file not found
let mut s = String::new();
f.read_to_string(&mut s)?; // Propagates error if read fails
Ok(s)
}
fn main() {
// Create a dummy file for the example to work
// In a real scenario, this file might not exist
std::fs::write("hello.txt", "CoddyKit User").unwrap();
match read_username_from_file() {
Ok(username) => println!("Username: {}", username),
Err(e) => println!("Error reading username: {}", e),
}
// Clean up the dummy file
std::fs::remove_file("hello.txt").unwrap();
}أنواع الأخطاء المخصصة
أحيانًا لا تكون أنواع الأخطاء المضمنة محددة بما يكفي. يمكنكم تعريف أنواع أخطاء مخصصة باستخدام التعدادات لتمثيل حالات الفشل المختلفة في تطبيقكم.
يجعل ذلك رسائل الأخطاء أوضح، ويتيح للجهات المستدعية التعامل مع أخطاء محددة بطرق مختلفة.
من الأنماط الشائعة إنشاء تعداد يسرد جميع الأخطاء المحتملة التي قد تنتجها دالة أو وحدة.
جعل `?` يعمل مع الأخطاء المخصصة
لكي يحوّل المعامل ? تلقائيًا خطأً منخفض المستوى (مثل io::Error) إلى نوع الخطأ المخصص لديكم، يجب أن يطبّق تعداد الخطأ المخصص سمة From لهذا الخطأ منخفض المستوى.
يخبر ذلك Rust بكيفية تحويل نوع خطأ إلى نوع آخر، مما يجعل تمرير الأخطاء سلسًا.
use std::io;
use std::fs::File;
use std::io::Read;
#[derive(Debug)]
enum MyError {
Io(io::Error),
Parse(std::num::ParseIntError)
}
impl From<io::Error> for MyError {
fn from(error: io::Error) -> Self {
MyError::Io(error)
}
}
impl From<std::num::ParseIntError> for MyError {
fn from(error: std::num::ParseIntError) -> Self {
MyError::Parse(error)
}
}
fn read_config(path: &str) -> Result<i32, MyError> {
let mut file = File::open(path)?; // io::Error converted to MyError::Io
let mut contents = String::new();
file.read_to_string(&mut contents)?; // io::Error converted to MyError::Io
let value = contents.trim().parse::<i32>()?; // ParseIntError converted to MyError::Parse
Ok(value)
}
fn main() {
// Create a dummy file for the example
std::fs::write("config.txt", "123").unwrap();
match read_config("config.txt") {
Ok(val) => println!("Config value: {}", val),
Err(e) => println!("Failed to read config: {:?}", e),
}
std::fs::remove_file("config.txt").unwrap(); // Clean up
// Example with a non-existent file
match read_config("non_existent.txt") {
Ok(val) => println!("Config value: {}", val),
Err(e) => println!("Failed to read config: {:?}", e),
}
}تحققوا من فهمكم
تأملوا مقتطف كود Rust التالي. ما الناتج إذا لم يكن الملف data.txt موجودًا؟
مراجعة: معالجة الأخطاء بطريقة متينة
لقد أتقنتم معالجة الأخطاء بطريقة متينة في Rust!
- تعداد
Result: يميز بينOk(T)عند النجاح وErr(E)عند الفشل. match: الطريقة الأساسية للتعامل مع متغيراتResult.- معامل
?: اختصار قوي لتمرير قيمErrعبر مكدس الاستدعاءات. - الأخطاء المخصصة: عرّفوا أنواع الأخطاء الخاصة بكم باستخدام التعدادات لمزيد من الوضوح والتعامل المحدد.
- سمة
From: تتيح تحويل الأخطاء منخفضة المستوى بسلاسة إلى أنواع الأخطاء المخصصة عند استخدام?.
استكشفوا بعد ذلك ميزات متقدمة في نظام الأنواع!
الأسئلة الشائعة
هل درس «معالجة الأخطاء المتينة باستخدام `Result`» مجاني؟
نعم — نص درس «معالجة الأخطاء المتينة باستخدام `Result`» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Learn Rust Coding، انتقل إلى CoddyKit PRO. تتضمن دورة Learn Rust Coding 3 دروس في المجموع.
ماذا ستتعلم في «معالجة الأخطاء المتينة باستخدام `Result`»؟
طبّقوا معالجة الأخطاء وفق أسلوب Rust باستخدام تعداد `Result` والعامل `?` وأنواع الأخطاء المخصّصة لبناء تطبيقات متينة. تتمرن على Learn Rust Coding مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Learn Rust Coding؟
لا تُشترط خبرة سابقة. Learn Rust Coding على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 3.
كم من الوقت يستغرق درس «معالجة الأخطاء المتينة باستخدام `Result`»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Learn Rust Coding هذا؟
نعم. كل درس في Learn Rust Coding يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- تنظيم الشيفرة باستخدام الوحدات
- إدارة الاعتماديات باستخدام Crates
- معالجة الأخطاء المتينة باستخدام `Result`