06_Cairo 中的Option(特殊Enum)

06_Cairo 中的 Option (特殊 Enum)

与 Rust 一样，Cairo 同样没有 Null 这种代表空的系统级变量或者属性。因为这样很容易出现：将空值当作非空值，或者将非空值当作空值的错误。

为了更好的理解这个错误，举一个 Golang 的例子：

在使用 Golang 的 Map 的时候，经常会出现” 从一个状态为 nil 的 Map 中读取数据” 的错误，因为 Map 可能在运行时是一个 nil。nil 在 Golang 中就表示空值的意思。

在 Cairo 中就不会出现这类错误，因为 Cairo 编译器在编译的时候，就能够发现这类错误。实现这个效果的原因就是：Cairo 没有 Null 这种代表空的系统级变量或者属性，Cairo 使用一个特殊的 Enum 类型来实现非空判断 (Option)。

Option 的基本使用

标准库中是这么定义 Option 的

Copy
enum Option<T> {
Some: T,
None: (),
}

在实际编码中时可以这么定义 Option 类型的变量：

Copy
use option::OptionTrait;

fn main(){
let some_char = Option::Some('e');
let some_number: Option<felt252> = Option::None(());
let absent_number: Option<u32> = Option::Some(8_u32);
}

Option 的 Some 成员是泛型，所以可以装入任意类型，上面我们就将一个短字符串装入了 Some 里。另外需要注意的是，如果使用的是 None，参数不可以为空，需要传入单位类型 ()。单位类型是 Cairo 中的一个特殊类型，它是用来保证它所在的位置的代码不会被编译 (编译时的空状态)。

与 Rust 的异同

与 Rust 相同点：

使用 None 时，需要指定 Option 中的类型，就像上面的 let some_number: Option<felt252> = Option::None(());，因为编译器通过 None 无法判断 Option 里面装的是什么类型。

与 Rust 不同点：

None 和 Some 不可以直接全局使用。

不可以将其他类型的变量和 Option 一起混用

Copy
use option::OptionTrait;

fn main() {
let x: u8 = 5_u8;
let y: Option<u8> = Option::Some(5_u8);

let sum = x + y;
}

// 得到如下错误：
error: Unexpected argument type. Expected: "core::integer::u8", found: "core::option::Option::<core::integer::u8>".
--> h04_enum_option.cairo:11:19
let sum = x + y;
^

虽然 Option 变量 y 中装的是一个 u8 的值，但是 y 和 x 的类型是不同的，所以不可以将两个相加。

Cairo 中的非空判断

Cairo 中所有类型的变量都是非空的，编译器会保证变量在任何时刻都不为空，就像上面的 x 变量。也就是说，我们在编写 Cairo 代码时，无需担心：我是否忘记检查我的变量在运行中是一个空值。唯一需要考虑是否是空值的地方，就是使用 Option 变量的时候。

换句话说，只有 Option 才可以获取到空值的这个状态（None）。我们可以通过 match 的控制模式来看看 Option 的实际使用案例。

源码剖析

Option 核心库的源码：https://github.com/starkware-libs/cairo/blob/main/corelib/src/option.cairo

4 个成员函数

有 4 个成员函数：

Copy
/// 判断Option中是否有值，如果有返回这个值；如果没有，抛出 err 错误
fn expect(self: Option<T>, err: felt252) -> T;

/// 判断Option中是否有值，如果有返回这个值；如果没有，也会抛出错误，此错误不是自定义的错误
fn unwrap(self: Option<T>) -> T;

/// 如果Option有值，返回true
fn is_some(self: @Option<T>) -> bool;

/// 如果Option没有值，返回false
fn is_none(self: @Option<T>) -> bool;