مدیریت خطا در Rust با Result و Option
آموزش

مدیریت خطا در Rust با Result و Option

زبان Rust به جای ساختارهای سنتی استثنا از نوع‌های Result و Option برای مدیریت شفاف و پیش‌بینی‌پذیر خطاها در زمان کامپایل استفاده می‌کند.

تحریریه‌ی دینا کد
نویسنده
۶ دقیقه مطالعه۰ بازدید
اشتراک گذاری۰
فهرست مطالب
  1. ۱چرا Rust از سیستم استثنا دوری می‌کند
  2. ۲بررسی عملی باز کردن فایل با دستور match
  3. ۳جایگزین‌های خواناتر به جای نوشتن matchهای تو در تو
  4. ۴خطای رایج برنامه‌نویسان در استفاده از unwrap
  5. ۵قدم بعدی برای تسلط بر خطاهای Rust

اگر با زبان‌هایی مثل جاوا، پایتون یا سی‌شارپ کار کرده باشید، احتمالاً عادت دارید هنگام بروز خطا از سازوکار استثنا (Exception) و بلوک‌های try-catch استفاده کنید. در چنین سیستمی اگر فراموش کنید خطایی را مهار کنید، برنامه در زمان اجرا متوقف می‌شود. این توقف ناگهانی هزینه‌های سنگینی در محیط واقعی به همراه دارد.

زبان Rust فلسفه متفاوتی را انتخاب کرده است؛ مفهومی به نام استثنا در این زبان وجود ندارد. به جای پنهان کردن خطرات، کامپایلر شما را وادار می‌کند همه مسیرهای احتمالی شکست یا فقدان مقدار را همان زمان نوشتن کد در نظر بگیرید. این هدف با دو ساختار داده بنیادی Option و Result تأمین می‌شود.

چرا Rust از سیستم استثنا دوری می‌کند

پرتاب استثنا مسیر اجرای کد را نامرئی تغییر می‌دهد. وقتی تابعی را صدا می‌زنید، مشخص نیست دقیقاً چه خطاهایی ممکن است از اعماق فراخوانی‌ها به سمت بالا پرتاب شوند، مگر اینکه تک‌تک مستندات را بررسی کنید. علاوه بر این، ارجاع به مقادیر تهی یا همان null منشأ خطاهای پرهزینه‌ای است که حافظه را به هم می‌ریزند.

زبان Rust مفهوم مقدار تهی ندارد. اگر مقداری ممکن است وجود نداشته باشد، از نوع شمارشی Option استفاده می‌کنید که دو حالت دارد:

  • Some(T) یعنی مقدار معتبری از نوع T وجود دارد.
  • None یعنی هیچ مقداری در دست نیست.

اگر عملیاتی با شکست روبه‌رو شود و نیاز باشد دلیل خطا را بررسی کنید، نوع Result به کار می‌آید. این شمارشی دو حالت مشخص دارد:

rustenum Result<T, E> {
    Ok(T),
    Err(E),
}

پارامتر T نوع مقدار در صورت موفقیت است و E ساختاری است که اطلاعات خطا را نگهداری می‌کند. تا زمانی که این دو حالت را بررسی نکنید، به داده داخل Ok دسترسی نخواهید داشت.

بررسی عملی باز کردن فایل با دستور match

وقتی تابعی مثل باز کردن فایل را اجرا می‌کنید، کامپایلر نوع بازگشتی را Result<File, io::Error> قرار می‌دهد. چون فایل ممکن است در مسیر خواسته شده نباشد یا مجوز خواندن آن را نداشته باشید. ساده‌ترین راه برای تفکیک این دو حالت، به کار بردن عبارت match است.

rustuse std::fs::File;
use std::io::ErrorKind;

fn main() {
    let file_result = File::open("config.txt");

    let _file = match file_result {
        // فایل پیدا شد و دسته دسترسی دریافت شد
        Ok(handle) => handle,
        // فایل باز نشد و خطا بررسی می‌شود
        Err(error) => match error.kind() {
            ErrorKind::NotFound => match File::create("config.txt") {
                Ok(created) => created,
                Err(e) => panic!("خطا در ایجاد فایل: {e:?}"),
            },
            other_error => {
                panic!("خطای دیگر در باز کردن فایل: {other_error:?}");
            }
        },
    };
}

اگر فایل وجود نداشته باشد، خروجی به شکل کنترل‌شده بررسی می‌شود و برنامه به جای کرش ناگهانی، اقدام به ساخت فایل می‌کند. اگر هم سیستم دسترسی اجازه ساخت را ندهد، ماکروی panic با پیام دقیق فعال می‌شود.

جایگزین‌های خواناتر به جای نوشتن matchهای تو در تو

نوشتن عبارت‌های طولانی match گاهی بدنه کد را شلوغ می‌کند. کتابخانه استاندارد توابع کمکی کاربردی مانند unwrap_or_else را برای حل این مسئله تدارک دیده است:

rustuse std::fs::File;
use std::io::ErrorKind;

fn main() {
    let _file = File::open("config.txt").unwrap_or_else(|error| {
        if error.kind() == ErrorKind::NotFound {
            File::create("config.txt").unwrap_or_else(|e| {
                panic!("امکان ساخت فایل وجود نداشت: {e:?}");
            })
        } else {
            panic!("خطای سیستمی: {error:?}");
        }
    });
}

این فرمول ساختار تمیزتری می‌سازد و دقیقاً همان رفتار منطقی نمونه قبلی را حفظ می‌کند.

خطای رایج برنامه‌نویسان در استفاده از unwrap

بسیاری از افراد هنگام شروع کار با این زبان، پشت سر هر متد بازگرداننده خطا از unwrap() یا expect() استفاده می‌کنند. متد unwrap می‌گوید اگر مقدار Ok بود آن را بیرون بکش، و اگر Err بود بلافاصله برنامه را با خطا متوقف کن.

استفاده افراطی از این متد در کدهای واقعی، مزیت اصلی سیستم خطای این زبان را بی‌اثر می‌کند. متد unwrap تنها برای اسکریپت‌های آزمایشی، نمونه‌های اولیه کوتاه یا مواردی مناسب است که از نظر منطق ریاضی مطمئن هستید خطا هرگز رخ نمی‌دهد.

قدم بعدی برای تسلط بر خطاهای Rust

گام بعدی در مسیر یادگیری، آشنایی با عملگر علامت سؤال (?) است که اجازه می‌دهد خطای توابع را به تابع بالادستی پاس بدهید بدون اینکه نیاز به زنجیره‌ای از شرط‌ها داشته باشید. برای پیاده‌سازی گام‌به‌گام این مفاهیم همراه با پروژه‌های عملی، می‌توانید سرفصل‌های دوره راست از صفر تا برنامه‌نویس را بررسی کنید.

تحریریه‌ی دینا کد
نویسنده

نظرها

اولین نفری باشید که نظر می‌دهد
برای ثبت نظر وارد شوید

سؤالتان را بپرسید یا تجربه‌تان را بنویسید تا نویسنده‌ی مطلب پاسخ بدهد.

مطالب مرتبط

از همین دسته
تفاوت واحدهای px و em و rem در CSS و کاربرد هرکدام
آموزش

تفاوت واحدهای px و em و rem در CSS و کاربرد هرکدام

واحدهای px، em و rem هرکدام برای بخش مشخصی از صفحه‌آرایی وب طراحی شده‌اند و انتخاب نادرست آن‌ها مقیاس‌پذیری و دسترسی‌پذیری سایت را مختل می‌کند.

تحریریه‌ی دینا کد۶ دقیقه
دستور GROUP BY در SQL چطور کار می‌کند
آموزش

دستور GROUP BY در SQL چطور کار می‌کند

عملکرد دستور GROUP BY در SQL و نحوه گروه‌بندی سطرها به همراه توابع تجمیعی را با مثال‌های واقعی یاد بگیرید.

تحریریه‌ی دینا کد۵ دقیقه
مدیریت خطا در Rust با Result و Option — دینا کد