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 的
enum Option<T> {
Some: T,
None: (),
}
在實際編碼中時可以這麼定義 Option 類型的變量:
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 一起混用#
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 個成員函數:
/// 判斷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;