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();
}

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。