StarknetAstro

StarknetAstro

08_Cairo 中的Match控制模式

08_Cairo 中的 Match 控制模式#

此文章使用的 Cairo 編譯器版本:2.0.0-rc0。因為 Cairo 正在快速更新,所以不同版本的語法會有些許不同,未來將會將文章內容更新到穩定版本。

Cairo 中的 Match 控制模式,90% 與 Rust 的一樣。因為 Cairo 還在開發中,許多特性還未完善,Rust 相對穩定,所以我們可以參考Rust 中的 Match 控制模式來對照著學習 Cairo 的這個功能。

基本用法#

如果匹配到的代碼塊比較簡短,可以不用使用大括號,並且以逗號分隔。如果比較長,那麼就需要使用大括號。

use debug::PrintTrait;

#[derive(Drop)]
enum Coin {
    Penny:(),
    Nickel:(),
    Dime:(),
    Quarter:(),
}

fn value_in_cents(coin: Coin) -> felt252 {
    match coin {
        Coin::Penny(_) => 1,
        Coin::Nickel(_) => 5,
        Coin::Dime(_) => 10,
        Coin::Quarter(_) => 25,
    }
}

fn main() {
	let p = Coin::Penny(());
	let n = Coin::Nickel(());
	value_in_cents(p).print();
	value_in_cents(n).print();
}

上述代碼中,value_in_cents的參數類型是 Coin,而 Coin 的子類型的變量也可以作為value_in_cents的入參。之前說過,Enum 可以理解為一組子類型的集合,所有子類型都可以代表父類型。所以任何一個子類型變量,都可以作為 value_in_cents 函數的參數。

使用大括號的情況:

fn value_in_cents(coin: Coin) -> felt252 {
    match coin {
        Coin::Penny(_) => {
            'Lucky penny!'.print();
            1
        },
        Coin::Nickel(_) => 5,
        Coin::Dime(_) => 10,
        Coin::Quarter(_) => 25,
    }
}

與 Rust 不一樣的地方#

(1). Enum 定義時的區別

  • 指定 Enum 元素類型的方式不同,Cairo 需要加上冒號Penny:(u8),Rust 不需要冒號Penny(u8)
  • 不指定 Enum 元素類型時,Cairo 不可以省略括號Penny:(),Rust 可以直接省略

(2). Match 匹配條件中的區別

  • 沒有指定類型的 Enum 元素,必須使用(_)標明,Rust 不需要

(3). 傳參時的區別

  • 沒有指定類型的 Enum 元素,傳參時需要傳入一個標準類型,比如:let n = Coin::Nickel(());
Enum 定義時Match 匹配條件傳參時
RustPenny(u8)Coin::PennyCoin::Penny()
CairoPenny:(u8)Coin::Penny(_)Coin::Penny(())

綁定參數#

Enum 是可以添加類型的,Match 的各個匹配項可以將 Enum 綁定的類型作為匹配項的參數。

use debug::PrintTrait;

// #[derive(Debug)] // so we can inspect the state in a minute
enum UsState {
    Alabama: (),
    Alaska: (),
// --snip--
}

enum Coin {
    Penny: (),
    Nickel: (),
    Dime: (),
    // 這裡添加了一個Enum類型
    Quarter: UsState,
}

impl UsStatePrintImpl of PrintTrait<UsState> {
    fn print(self: UsState) {
        match self {
            UsState::Alabama(_) => ('Alabama').print(),
            UsState::Alaska(_) => ('Alaska').print(),
        }
    }
}

fn value_in_cents(coin: Coin) -> felt252 {
    match coin {
        Coin::Penny(_) => 1,
        Coin::Nickel(_) => 5,
        Coin::Dime(_) => 10,
        Coin::Quarter(state) => {
            state.print();
            25
        }
    }
}

fn main() {
    let u = Coin::Quarter(UsState::Alabama(()));

    value_in_cents(u);
}

上面代碼中,UsState::Alabama 就成了 Coin::Quarter (state) 中的參數 state。

與 Rust 不一樣的地方#

Cairo 中 Enum 默認是沒有實現 PrintTrait 的,所以需要為 UsState 添加一個 impl,用來實現打印功能

impl UsStatePrintImpl of PrintTrait::<UsState> {
    fn print(self: UsState) {
        match self {
            UsState::Alabama(_) => ('Alabama').print(),
            UsState::Alaska(_) => ('Alaska').print(),
        }
    }
}

Match 模式與 Option 搭配使用#

Match 模式與 Option 搭配使用,就能夠實現非空判斷。

我們嘗試實現一個函數,包含邏輯:如果參數不為空,就 + 1,如果為空,就不做任何操作。

use option::OptionTrait;
use debug::PrintTrait;

fn plus_one(x: Option<u8>) -> Option<u8> {
    match x {
        Option::Some(val) => Option::Some(val + 1_u8),
        Option::None(_) => {
            // 這裡可以增加一些額外操作 ...
            Option::None(())
        },
    }
}

fn main() {
    let five: Option<u8> = Option::Some(5_u8);
    let six: Option<u8> = plus_one(five);
    six.unwrap().print();

    let none = plus_one(Option::None(()));
    if none.is_none() {
        'is none !'.print();
    }
}

plus_one函數就實現了這個功能,可以在函數內部增加一些為空的邏輯處理,也可以在調用處通過返回值來判斷是否為空,進而處理為空的情況。

使用 Match 模式的規則#

第一個規則:Match 需要覆蓋所有的可能。

use option::OptionTrait;
use debug::PrintTrait;

fn plus_one(x: Option<u8>) -> Option<u8> {
    match x {
        Option::Some(i) => Option::Some(i + 1_u8), 
    }
}

fn main() {
    let five: Option<u8> = Option::Some(5_u8);
    let six = plus_one(five);
    let none = plus_one(Option::None(()));
}

以上代碼沒有處理 None 的情況,所以編譯會報錯。

第二個規則:Cairo 目前只有很簡單的 Default 效果。

use option::OptionTrait;
use debug::PrintTrait;

fn match_default(x: felt252) -> felt252 {
    match x {
        0 => 'zero', 
        _ => 'default',
    }
}

fn main() {
    let r = match_default(0);
    r.print();
    let r = match_default(1);
    r.print();
}

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。