Cairo تکنیکهایی برای مدیریت خطا ارائه میدهد تا بتوانید مشکلات را برطرف کنید و خطاها را مدیریت کنید و کد در حین اجرا روان پیش برود.
در Cairo دو نوع خطا وجود دارد:
panic: این خطاها برنامه را بلافاصله متوقف میکنند.Result: این خطاها را میتوان در زمان اجرا مدیریت کرد.panic
در Cairo ممکن است در طول اجرای برنامه مشکلات پیشبینینشدهای پیش بیاید که به خطاهای زمان اجرا موسوم به «panic» منجر شود. این وضعیت اجرای برنامه را متوقف میکند و همین زمانی به کار میآید که ادامه دادن هیچ معنایی نداشته باشد. یک panic ممکن است ناخواسته رخ دهد، از طریق کارهایی که باعث panic در کد میشوند (مثلاً دسترسی به آرایهای فراتر از مرزهایش)، یا بهطور عمدی، با فراخوانی تابع panic.
fn main() {
let data = array![1, 2];
if true {
panic(data);
}
println!("This line will not be reached.");
}
اجرای این مثال خروجی زیر را در کنسول چاپ میکند:
Run panicked with [1, 2, ].
ماکروی ویژهای هم وجود دارد به نام panic! که اجازه میدهد از یک ByteArray به عنوان پیام خطا استفاده شود.
panic!("The error for the panic! Error message is not limited to 31 characters anymore");
assert!
ماکروی assert! ابزار مفیدی است برای اعمال شرطهای مشخص در کد شما. اگر شرط داخل assert! به false ارزیابی شود، برنامه با یک پیام خطای ByteArray دچار panic میشود. از این ماکرو اغلب برای بررسی فرضها در طول توسعه و اطمینان از برآورده شدن معیارهای مشخص توسط مقادیر استفاده میشود.
برای نمونه:
fn main() {
let x = 5;
assert!(x > 0, "x must be greater than zero");
}
اگر x بزرگتر از صفر نباشد، برنامه با پیام "x must be greater than zero" دچار panic میشود. assert! برای بررسی ناورداها و پیششرطها بدون نوشتن دستی کد مدیریت خطا مفید است.
nopanic
نشانهگذاری nopanic در Cairo نشان میدهد که یک تابع هرگز دچار panic نمیشود.
fn function_never_panic() -> felt252 nopanic {
108
}
یک تابع nopanic را فقط میتوان در تابعی فراخوانی کرد که آن هم با nopanic نشانهگذاری شده باشد.
panic_with
صفت panic_with در Cairo تابعی را نشانهگذاری میکند که Option یا Result برمیگرداند و یک تابع پوششی برای تابع نشانهگذاریشدهی شما میسازد که اگر تابع Option::None یا Result::Err را برگرداند، دچار panic میشود.
#[panic_with('value is 0', wrap_not_zero)]
fn check_not_zero(value: u128) -> Option<u128> {
if value == 0 {
Option::None
} else {
Option::Some(value)
}
}
fn main() {
check_not_zero(0); // -> returns Option::None
wrap_not_zero(0); // -> panics with 'value is 0'
}
Result
نوع شمارشی Result خطاها را به شکلی سنجیده مدیریت میکند و به جای کرش کردن یا تولید رفتار تعریفنشده، بازخورد ارائه میدهد.
enum Result<T, E> {
Ok: T,
Err: E,
}
بیایید ببینیم چگونه میتوانیم از این شمارشی برای مدیریت تقسیم بر صفر استفاده کنیم:
fn divide(a: u32, b: u32) -> Result<u32, ByteArray> {
if b == 0 {
return Result::Err("Error: Division by zero"); // Return an error if division by zero is attempted
}
Result::Ok(a / b)
}
fn main() {
// Test with division by zero
match divide(10, 0) {
Result::Ok(result) => println!("Division successful: {}", result),
Result::Err(e) => println!("{}", e),
}
}
?
عملگر ? برای مدیریت خطای موجز و ضمنی به کار میرود و اجازه میدهد تابع فراخوانکننده با هر خطایی که ممکن است رخ دهد سر و کار پیدا کند. وقتی از عملگر ? روی یک نوع Result یا Option استفاده میکنید، کارهای زیر را انجام میدهد:
Result::Ok(x) یا Option::Some(x) باشد، مقدار درونی x را مستقیماً برمیگرداند.Result::Err(e) یا Option::None باشد، با بازگشت فوری از تابع، خطا یا None را منتشر میکند.fn parse_u8(s: felt252) -> Result<u8, felt252> {
match s.try_into() {
Option::Some(value) => Result::Ok(value),
Option::None => Result::Err('Invalid integer'),
}
}
fn do_something_with_parse_u8(input: felt252) -> Result<u8, felt252> {
let input_to_u8: u8 = parse_u8(input)?;
// DO SOMETHING
let res = input_to_u8 - 1;
Result::Ok(res)
}
fn main() {
let number: felt252 = 258;
match do_something_with_parse_u8(number) {
Result::Ok(value) => println!("Result: {}", value),
Result::Err(e) => println!("Error: {}", e),
}
}