2. کنترل خطا و ایمنی
تفاوت panic! و Result چیست؟ چه زمانی از هرکدام استفاده میکنید؟
پاسخ بلند
panic! بیانگر یک شکست غیرقابلگردش (unrecoverable) است. زمانی استفاده میشود که برنامه وارد وضعیتی شده که ادامهٔ اجرا منطقی نیست؛ معمولاً نقض یک invariant داخلی، باگ برنامهنویسی، یا شرایطی که برنامه از ابتدا فرض کرده هرگز رخ نمیدهد. هنگام panic، بسته به تنظیمات پروژه، یا استک unwind میشود یا کل فرآیند abort میشود.
در مقابل، Result<T, E> برای خطاهای قابلگردش (recoverable) طراحی شده است. این نوع خطاها بخشی از رفتار طبیعی سیستم هستند: خطاهای I/O، خطاهای شبکه، خطاهای parsing یا validation. با استفاده از Result، خطا در امضای تابع منعکس میشود و caller مجبور است بهصورت صریح آن را مدیریت یا propagate کند.
قاعدهٔ طراحی:
Resultبرای خطاهای قابل انتظار و بخشی از دامنهٔ مسئلهpanic!برای نقض فرضیات داخلی و باگها
در کتابخانهها و APIهای عمومی تقریباً همیشهResultترجیح داده میشود.
پاسخ کوتاه
Result برای خطاهای قابلهندل است؛ panic! برای باگها و شرایطی که ادامهٔ اجرا بیمعنی است.
مفهوم unwinding و abort در panic چیست؟
پاسخ بلند
Rust دو استراتژی برای واکنش به panic دارد:
-
Unwinding: استک بهتدریج باز میشود و destructorها (
Drop) اجرا میشوند. این رفتار امکان cleanup منابع را فراهم میکند و باcatch_unwindمیتوان panic را در مرزهای مشخص مهار کرد. با این حال، هزینهٔ اجرایی دارد و در تعامل با FFI میتواند پیچیده باشد. -
Abort: فرآیند بلافاصله خاتمه مییابد، بدون اجرای destructorها. این روش سریعتر و قابلپیشبینیتر است و معمولاً در سیستمهای embedded، real-time یا سناریوهایی که unwind ناامن است، استفاده میشود.
انتخاب بین این دو از طریق تنظیم panic = "unwind" یا panic = "abort" انجام میشود و یک تصمیم معماری محسوب میشود.
پاسخ کوتاه
unwinding استک را باز کرده و destructorها را اجرا میکند؛ abort فوراً برنامه را متوقف میکند بدون cleanup.
الگوی ? چگونه کار میکند؟
پاسخ بلند
عملگر ? مکانیزمی برای propagate کردن خطا بهشکل صریح و مختصر است. اگر مقدار از نوع Result باشد:
- در صورت
Ok، مقدار unwrap شده و ادامهٔ اجرا انجام میشود - در صورت
Err، تابع بلافاصله با همان خطا (یا خطای تبدیلشده باFrom) برمیگردد
بهصورت مفهومی، ? جایگزین یک match صریح است و از traitهای مربوط به error propagation در زبان استفاده میکند. این الگو خوانایی را افزایش داده و کد را composable میکند. همین رفتار برای Option نیز صادق است.
پاسخ کوتاه
? در صورت خطا مقدار را زودهنگام برمیگرداند و در صورت موفقیت مقدار را unwrap میکند.
چرا Rust خطاها را در type system مدل میکند؟
پاسخ بلند
Rust خطاها را بخشی از مدل نوع میداند تا مدیریت آنها:
- صریح باشد و امکان نادیدهگرفتن وجود نداشته باشد
- در زمان کامپایل بررسی شود
- بهصورت ترکیبپذیر بین لایههای مختلف سیستم propagate شود
- مستندسازی طبیعی API را شکل دهد
این رویکرد مانع بروز خطاهای کلاسیک runtime (مانند null dereference) میشود و باعث افزایش reliability، testability و maintainability سیستم میگردد؛ بهویژه در سیستمهای concurrent و توزیعشده.
پاسخ کوتاه
برای صریحبودن، بررسی در زمان کامپایل و افزایش قابلیت اطمینان؛ خطا بخشی از قرارداد type میشود.
چه زمانی استفاده از unwrap() قابل قبول است؟
پاسخ بلند
unwrap() در صورت نبود مقدار باعث panic! میشود و به همین دلیل در کد production بهطور کلی نامطلوب است. با این حال، استفاده از آن در شرایط زیر قابل قبول است:
- تستها و benchmarkها
- نمونهکدها و مستندات
- prototypeهای کوتاهعمر
- جاهایی که invariantها بهطور قطعی برقرار شدهاند و failure نشاندهندهٔ باگ است
در این موارد، expect() با پیام توضیحی معمولاً انتخاب بهتری نسبت به unwrap() خام است. در APIهای عمومی و کتابخانهها، استفاده از unwrap() توصیه نمیشود.
پاسخ کوتاه
در تستها، نمونهکدها یا زمانی که invariantها تضمین شدهاند؛ در کد تولیدی بهتر است از Result یا دستکم expect استفاده شود.