مقدمه
به فصل «مدیریت خطا» خوش آمدید. زبان Rust به دلیل تمرکز بر روی استحکام و قابلیت اطمینان، رویکرد
بسیار دقیقی به مدیریت خطا دارد. در Rust، خطاها به دو دسته اصلی تقسیم میشوند: خطاهای «قابل
بازیابی» (recoverable) و خطاهای «غیرقابل بازیابی» (unrecoverable).
- خطاهای قابل بازیابی: اینها خطاهایی هستند که انتظار وقوع آنها میرود و برنامه باید
بتواند به صورت منطقی به آنها واکنش نشان دهد. برای مثال، تلاش برای باز کردن فایلی که وجود
ندارد. در این موارد، ما معمولاً میخواهیم خطا را به کد فراخواننده برگردانیم تا او تصمیم
بگیرد چه کاری انجام دهد. در Rust، این نوع خطاها با استفاده از نوع Result<T, E> مدیریت
میشوند.
- خطاهای غیرقابل بازیابی: اینها خطاهایی هستند که نشاندهنده یک باگ یا یک وضعیت
غیرمنتظره و بحرانی در برنامه هستند که ادامه اجرای برنامه از آن نقطه به بعد، ایمن یا منطقی
نیست. برای مثال، تلاش برای دسترسی به یک ایندکس خارج از محدوده در یک آرایه. در این موارد،
Rust با استفاده از ماکروی panic! اجرای برنامه را متوقف میکند.
در این درس، ما بر روی دسته دوم، یعنی خطاهای غیرقابل بازیابی و مکانیزم panic تمرکز خواهیم
کرد.
متوقف کردن اجرا با ماکروی panic!
وقتی ماکروی panic! اجرا میشود، برنامه شما یک پیام خطا چاپ میکند،
حافظه stack را پاکسازی
میکند (unwinding) و سپس از برنامه خارج میشود. این سادهترین راه برای مدیریت یک وضعیت خطای
بحرانی است.
شما میتوانید به صورت دستی و در هر جای کدتان، panic! را فراخوانی
کنید.
src/main.rs
fn main() {
panic!("Farewell, cruel world!");
}
اجرای این کد، خروجی مشابه زیر را تولید خواهد کرد:
$ cargo run
Compiling panic v0.1.0 (...)
Finished dev [unoptimized + debuginfo] target(s) in ...s
Running `target/debug/panic`
thread 'main' panicked at 'Farewell, cruel world!', src/main.rs:2:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
همانطور که میبینید، پیام پنیک شامل پیامی است که ما ارائه کردهایم و همچنین مکان دقیق وقوع آن در
کد منبع.
ردیابی منشأ خطا با Backtrace
پیام خطای panic! به ما میگوید که در کدام خط از کد مشکل رخ داده
است، اما همیشه نمیگوید
چرا. برای فهمیدن این موضوع، ما نیاز به یک backtrace داریم. backtrace لیستی
از تمام
توابعی است که تا لحظه وقوع خطا فراخوانی شدهاند. این لیست به ما کمک میکند تا مسیر اجرای کد را
ردیابی کرده و منشأ باگ را پیدا کنیم.
همانطور که در پیام خطای قبلی اشاره شده، برای دریافت backtrace، باید متغیر محیطی
RUST_BACKTRACE را برابر با `1` قرار داده و سپس برنامه را اجرا کنیم. بیایید یک مثال
پیچیدهتر
را بررسی کنیم که در آن، panic به صورت غیرمستقیم و توسط کد کتابخانه استاندارد رخ میدهد.
src/main.rs
fn main() {
let v = vec![1, 2, 3];
v[99];
}
اجرای این کد با cargo run منجر به یک پنیک میشود. حالا بیایید آن را با فعال کردن
backtrace
اجرا کنیم:
$ RUST_BACKTRACE=1 cargo run
خروجی شما بسته به سیستم عامل و نسخه Rust ممکن است کمی متفاوت باشد، اما ساختار کلی آن شبیه به این
خواهد بود:
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', src/main.rs:4:5
stack backtrace:
0: rust_begin_unwind
at /rustc/b.../library/std/src/panicking.rs:597:5
1: core::panicking::panic_fmt
at /rustc/b.../library/core/src/panicking.rs:64:14
2: core::panicking::panic_bounds_check
at /rustc/b.../library/core/src/panicking.rs:102:5
3: <usize as core::slice::index::SliceIndex<[T]>>::index
at /rustc/b.../library/core/src/slice/index.rs:242:10
4: core::slice::index::<impl core::ops::index::Index<I> for [T]>::index
at /rustc/b.../library/core/src/slice/index.rs:18:9
5: <alloc::vec::Vec<T,A> as core::ops::index::Index<I>>::index
at /rustc/b.../library/alloc/src/vec/mod.rs:2737:9
6: panic::main
at ./src/main.rs:4:5
... (more lines)
با خواندن این backtrace از پایین به بالا، میتوانیم مسیر اجرای کد را دنبال کنیم. خط شماره
۶ به
ما میگوید که پنیک از خط ۴ فایل src/main.rs ما شروع شده است. خطوط بالاتر، توابع داخلی
Rust را نشان میدهند که توسط v[99] فراخوانی شدهاند و در نهایت منجر
به تشخیص خطای "index out
of bounds" شدهاند. این اطلاعات برای دیباگ کردن بسیار ارزشمند است.
در این درس با مکانیزم panic! به عنوان روش Rust برای مدیریت خطاهای
غیرقابل بازیابی آشنا شدیم.
دیدیم که این خطاها معمولاً نشاندهنده یک باگ در برنامه هستند و چگونه میتوان با استفاده از
backtrace منشأ آنها را ردیابی کرد. در درس بعدی، به سراغ «خطاهای قابل بازیابی» خواهیم رفت
و با
نوع Result<T, E> به عنوان روش اصولی و قدرتمند Rust برای
مدیریت خطاهایی که انتظار وقوع آنها
میرود، آشنا خواهیم شد.