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属性Cairo 的panic_with属性用来标记返回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),
}
}