StarknetAstro

StarknetAstro

08_CairoのMatchコントロールモード

08_Cairo 中の Match 制御モード#

この記事で使用されている Cairo コンパイラのバージョン:2.0.0-rc0。Cairo は現在急速に更新されているため、異なるバージョンの構文が若干異なる場合がありますが、将来的には安定したバージョンに記事の内容を更新する予定です。

Cairo の Match 制御モードは、Rust のそれと 90% 同じです。Cairo はまだ開発中であり、多くの機能が未完成ですが、Rust は比較的安定しているため、この機能を学ぶ際にRust の Match 制御モードを参考にすることができます。

基本的な使い方#

マッチするコードブロックが比較的短い場合は、中括弧を使用せずにコンマで区切ることができます。長い場合は、中括弧を使用する必要があります。

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 を追加して Print 機能を実装する必要があります。

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

Match パターンと Option の組み合わせ#

Match パターンと Option を組み合わせると、非 null チェックを実現することができます。

次のようなロジックを含む関数を実装してみましょう:パラメータが null でない場合は + 1 し、null の場合は何もしない。

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関数はこの機能を実現し、関数内で null の場合のロジック処理を追加することもできます。また、返り値を使用して null かどうかを判断し、それに応じて null の場合の処理を行うこともできます。

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 の場合の処理がされていないため、コンパイルエラーが発生します。

2 番目のルール:Cairo には現在非常にシンプルなデフォルト効果しかありません。

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

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。