无法恢复的错误与恐慌(panic)
在Cairo中,程序执行过程中可能会出现意外问题,导致运行时错误。虽然核心库中的panic函数并没有为这些错误提供解决方案,但它确实承认这些错误的发生并终止程序。在Cairo中,有两种主要的方式可以触发panic:无意地通过导致代码panic的行为(例如,访问一个超出其界限的数组),或故意地,通过调用panic函数。
当发生恐慌时,它会导致程序突然终止。panic
函数接受一个数组作为参数,可以用来提供错误消息,并执行一个解除过程,在这个过程中所有变量都会被丢弃,字典被压缩,以确保程序的健全性,安全地终止执行。
下面是我们如何在一个程序中panic
并返回错误代码2
:
文件名: src/lib.cairo
use debug::PrintTrait; fn main() { let mut data = ArrayTrait::new(); data.append(2); if true == true { panic(data); } 'This line isn\'t reached'.print(); }
运行该程序将产生以下输出:
$ scarb cairo-run
Run panicked with [2 (''), ].
正如你在输出中所注意到的,打印语句没有被执行,因为程序在遇到panic
语句后就终止了。
Cairo 中处理恐慌的另一种更符合习惯的方法是使用 panic_with_felt252
函数。这个函数作为定义数组过程的抽象,通常更受欢迎,因为它表达意图更清晰、更简洁。通过使用 panic_with_felt252
,开发者可以通过提供一个 felt252 类型的错误消息作为参数,在一行代码中实现恐慌,使代码更易读和可维护。
让我们来考察一个例子:
fn main() { panic_with_felt252(2); }
执行这个程序会产生和之前一样的错误信息。在这种情况下,如果在返回错误是不需要一个数组和多个值,那么panic_with_felt252
是一个更简洁的选择。
nopanic记号
你可以使用nopanic
记号来表示一个函数永远不会恐慌。只有 nopanic
函数可以在标注为 nopanic
的函数中被调用。
例子:
fn function_never_panic() -> felt252 nopanic {
42
}
错误的例子:
fn function_never_panic() nopanic {
assert(1 == 1, 'what');
}
如果你写了以下函数,其中包括一个可能会panic的函数,你会得到以下错误:
error: Function is declared as nopanic but calls a function that may panic.
--> test.cairo:2:12
assert(1 == 1, 'what');
^****^
Function is declared as nopanic but calls a function that may panic.
--> test.cairo:2:5
assert(1 == 1, 'what');
^********************^
请注意,有两个函数可能会在这里发生panic,即断言和相等比较。
panic_with 属性
您可以使用 panic_with
属性来标记返回 Option
或 Result
的函数。该属性需要两个参数,即作为 panic 原因传递的数据以及包装函数的名称。它将为您标注的函数创建一个封装函数,如果函数返回 None
或 Err
,该封装函数将被调用,并使用给定的数据。
例子:
#[panic_with('value is 0', wrap_not_zero)] fn wrap_if_not_zero(value: u128) -> Option<u128> { if value == 0 { Option::None } else { Option::Some(value) } } fn main() { wrap_if_not_zero(0); // this returns None wrap_not_zero(0); // this panic with 'value is 0' }
使用断言(assert)
Cairo核心库中的assert函数实际上是一个基于panic的实用函数。它断言一个布尔表达式在运行时是真的,如果不是,它就会调用带有错误值的panic函数。assert函数需要两个参数:要验证的布尔表达式,以及错误值。错误值被指定为felt252,所以任何传递的字符串都必须能够容纳在felt252中。
下面是它的一个使用例子:
fn main() { let my_number: u8 = 0; assert(my_number != 0, 'number is zero'); 100 / my_number; }
我们在main
中断言my_number
不是0,以确保我们没有进行除以0的操作。
在这个例子中,my_number
是零,所以断言会失败,程序会panic,
并给出 'number is zero'的字符串结果(以felt252的形式),除法将不会被执行。