Unrecoverable Errors with panic
In Cairo, unexpected issues may arise during program execution, resulting in runtime errors. While the panic
function from the core library doesn't provide a resolution for these errors, it does acknowledge their occurrence and terminates the program. There are two primary ways that a panic can be triggered in Cairo: inadvertently, through actions causing the code to panic (e.g., accessing an array beyond its bounds), or deliberately, by invoking the panic
function.
当发生恐慌时,它会导致程序突然终止。panic
函数接受一个数组作为参数,可以用来提供错误消息,并执行一个解除过程,在这个过程中所有变量都会被丢弃,字典被压缩,以确保程序的健全性,安全地终止执行。
Here is how we can call panic
from inside a program and return the error code 2
:
文件名: src/lib.cairo
fn main() {
let mut data = array![2];
if true {
panic(data);
}
println!("This line isn't reached");
}
运行该程序将产生以下输出:
$ scarb cairo-run
Compiling no_listing_01_panic v0.1.0 (listings/ch09-error-handling/no_listing_01_panic/Scarb.toml)
Finished `dev` profile target(s) in 3 seconds
Running no_listing_01_panic
Run panicked with [2, ].
As you can notice in the output, the call to println!
macro is never reached, as the program terminates after encountering the panic
statement.
An alternative and more idiomatic approach to panic in Cairo would be to use the panic_with_felt252
function. This function serves as an abstraction of the array-defining process and is often preferred due to its clearer and more concise expression of intent. By using panic_with_felt252
, developers can panic in a one-liner by providing a felt252
error message as an argument, making the code more readable and maintainable.
让我们来考察一个例子:
use core::panic_with_felt252;
fn main() {
panic_with_felt252(2);
}
Executing this program will yield the same error message as before. In that case, if there is no need for an array and multiple values to be returned within the error, panic_with_felt252
is a more succinct alternative.
panic!
Macro
panic!
macro can be really helpful. The previous example returning the error code 2
shows how convenient panic!
macro is. There is no need to create an array and pass it as an argument like with the panic
function.
fn main() {
if true {
panic!("2");
}
println!("This line isn't reached");
}
Unlike the panic_with_felt252
function, using panic!
allows the input, which is ultimately the panic error, to be a literal longer than 31 bytes. This is because panic!
takes a string as a parameter. For example, the following line of code will successfully compile:
panic!("the error for panic! macro is not limited to 31 characters anymore");
nopanic
Notation
你可以使用nopanic
记号来表示一个函数永远不会恐慌。只有 nopanic
函数可以在标注为 nopanic
的函数中被调用。
下面是一个例子:
fn function_never_panic() -> felt252 nopanic {
42
}
This function will always return 42
and is guaranteed to never panic. Conversely, the following function is not guaranteed to never panic:
fn function_never_panic() nopanic {
assert(1 == 1, 'what');
}
If you try to compile this function that includes code that may panic, you will get the following error:
$ scarb cairo-run
Compiling no_listing_04_nopanic_wrong v0.1.0 (listings/ch09-error-handling/no_listing_05_nopanic_wrong/Scarb.toml)
error: Function is declared as nopanic but calls a function that may panic.
--> listings/ch09-error-handling/no_listing_05_nopanic_wrong/src/lib.cairo:4:12
assert(1 == 1, 'what');
^****^
error: Function is declared as nopanic but calls a function that may panic.
--> listings/ch09-error-handling/no_listing_05_nopanic_wrong/src/lib.cairo:4:5
assert(1 == 1, 'what');
^********************^
error: could not compile `no_listing_04_nopanic_wrong` due to previous error
error: `scarb metadata` exited with error
Note that there are two functions that may panic here, assert
and equality with ==
. We usually don't use assert
function in practice and use assert!
macro instead. We will discuss assert!
macro in more detail in the Testing Cairo Programs chapter.
panic_with
Attribute
You can use the panic_with
attribute to mark a function that returns an Option
or Result
. This attribute takes two arguments, which are the data that is passed as the panic reason as well as the name for a wrapping function. It will create a wrapper for your annotated function which will panic if the function returns None
or Err
, with the given data as the panic error.
例子:
#[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 panics with 'value is 0'
}