
روز ۲۷: Error Handling با Result در Rust
مدیریت خطا یکی از مهمترین تفاوتهای Rust با بسیاری از زبانهای برنامه نویسی رایج است و دلیل اصلی شهرت Rust به عنوان زبانی ایمن و قابل اعتماد محسوب میشود. در Rust برخلاف زبانهایی که به استثناها یا try-catch متکی هستند، مدیریت خطا به شکل کاملاً صریح و قابل پیشبینی انجام میشود. این رویکرد باعث میشود برنامهنویس از همان ابتدا مجبور به فکر کردن درباره حالتهای خطا باشد و از نادیده گرفتن آنها جلوگیری شود. در قلب این سیستم، نوع دادهای به نام Result قرار دارد که پایه و اساس Error Handling در Rust را تشکیل میدهد. یادگیری صحیح Result نقطه عطفی در مسیر حرفهای شدن با Rust است.
Result چیست و چرا در Rust اینقدر مهم است؟
Result یک enum جنریک در Rust است که برای نمایش نتیجه یک عملیات استفاده میشود؛ عملیاتی که ممکن است موفق باشد یا با خطا مواجه شود. این نوع داده دو حالت دارد: Ok که نشاندهنده موفقیت عملیات است و Err که بیانگر وقوع خطا میباشد. نکته کلیدی اینجاست که Rust شما را مجبور میکند هر دو حالت را در نظر بگیرید و این موضوع از بروز خطاهای پنهان در زمان اجرا جلوگیری میکند. برخلاف زبانهایی که خطاها را تا لحظه اجرا مخفی نگه میدارند، Rust همه چیز را در زمان کامپایل شفاف میکند و همین مسئله باعث پایداری بالای برنامهها میشود.
fn read_number(input: &str) -> Result<i32, String> {
input.parse::<i32>().map_err(|_| "Invalid number".to_string())
}
در این مثال، تابع یا یک عدد صحیح معتبر برمیگرداند یا یک پیام خطا. همین امضای تابع بهتنهایی به ما میگوید که امکان شکست وجود دارد و نباید آن را نادیده بگیریم.
استفاده از match برای مدیریت Result
سادهترین و شفافترین روش برای کار با Result استفاده از match است. با match میتوان دقیقاً مشخص کرد که در صورت موفقیت چه اتفاقی بیفتد و در صورت خطا چه واکنشی داشته باشیم. این روش خوانایی بالایی دارد و برای یادگیری اولیه Error Handling بسیار مناسب است. استفاده از match باعث میشود منطق مدیریت خطا کاملاً واضح و قابل دنبال کردن باشد، مخصوصاً در پروژههایی که هنوز پیچیدگی زیادی ندارند.
let result = read_number("42");
match result {
Ok(value) => println!("Parsed number: {}", value),
Err(error) => println!("Error occurred: {}", error),
}در اینجا هیچ حالت مبهمی وجود ندارد. یا مقدار صحیح داریم یا خطا، و هر دو مسیر کاملاً کنترل شدهاند. این شفافیت یکی از فلسفههای اصلی Rust است.
اپراتور ? و سادهسازی Error Handling
وقتی کد شما بزرگتر میشود، استفاده مداوم از match میتواند باعث شلوغی و کاهش خوانایی شود. Rust برای حل این مشکل اپراتور ? را معرفی کرده است. این اپراتور در صورت موفقیت مقدار داخل Ok را برمیگرداند و در صورت خطا، همان Err را به تابع فراخواننده منتقل میکند. استفاده درست از ? باعث میشود کد تمیزتر، کوتاهتر و حرفهایتر به نظر برسد.