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 標記Cairo 的nopanic標記表示某個函式永遠不會 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),
}
}