StarknetAstro

StarknetAstro

16_Cairo1.0中的泛型(Generic)

16_Cairo1.0 中的泛型 (Generic)#

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

泛型是一種編程語言特性,它允許在編寫代碼時使用類型參數,這些類型參數可以在代碼實例化時被具體類型替換。

實際編程中,我們設計好了一套算法來高效地處理業務上的一些問題。如果沒有泛型的話,每個類型就需要複製一份同一套算法的代碼。理想情況下,算法應是和數據結構以及類型無關的,各種特殊的數據類型理應做好自己分內的工作,算法只關心一個標準的實現。

所以,泛型很酷😎

Cairo 中,泛型可以用在:函數、結構體、枚舉 和 Trait 中的 Method 裡面。

函數中的泛型#

函數中如果傳入包含泛型的參數,需要在參數前面的 <> 中聲明泛型,而且這將作為函數簽名的一部分。接下來我們來實現一個” 找出泛型數組中最小的泛型元素” 的功能:

use debug::PrintTrait;
use array::ArrayTrait;

// PartialOrd 實現泛型變量之間的比較
fn smallest_element<T, impl TPartialOrd: PartialOrd<T>, impl TCopy: Copy<T>, impl TDrop: Drop<T>>(
    list: @Array<T>
) -> T {
	// 這裡使用了 * ,所以 T 必須實現了 copy trait
    let mut smallest = *list[0];

    let mut index = 1;

    loop {
        if index >= list.len() {
            break smallest;
        }
        // 這裡是兩個泛型變量之間的比較,需要實現 PartialOrd
        if *list[index] < smallest {
            smallest = *list[index];
        }
        index = index + 1;
    }
}

fn main() {
    let mut list: Array<u8> = ArrayTrait::new();
    list.append(5);
    list.append(3);
    list.append(10);

    let s = smallest_element(@list);
    assert(s == 3, 0);
    s.print();
}

可以看到,我們在泛型聲明區域,對泛型 T 添加了許多修飾(可以取名為 T,也可以取其他的名)。泛型可以是任意數據類型,如果數據類型能符合通用算法,勢必就會對傳入的數據類型有一定的約束,而我們在 <> 裡添加的 impl,就是對傳入此函數的泛型的約束。

  1. 首先,我們從 T 的 snapshot 中取值了,那麼 T 就必須實現了 Copy trait;
  2. 其次,T 類型變量 smallest 最終作為函數的返回值,返回到 main 函數中,這步即包含了 move 操作,同時也有 Drop 操作,所以需要實現 Drop trait;
  3. 最後,我們需要比較兩個泛型的大小,所以需要實現 PartialOrd trait。

所以我們看到函數中聲明泛型的部位有這麼一段: <T, impl TPartialOrd: PartialOrd<T>, impl TCopy: Copy<T>, impl TDrop: Drop<T>>。調用這個函數時,參數數組中的所有元素都必須實現這三個約束中描述的 trait。

結構體中的泛型#

結構體元素中也可以放入泛型字段,如:

struct Wallet<T> {
    balance: T
}

impl WalletDrop<T, impl TDrop: Drop<T>> of Drop<Wallet<T>>;
#[derive(Drop)]
struct Wallet<T> {
    balance: T
}

以上兩種方式,應該都可以。Cairo book 中說第二種方式不會將類型 T 声明為:實現了 Drop trait,但是沒有給出一些案例代碼。我實驗了幾次,目前都沒有發現有什麼區別,後續發現了再加上。

結構體 Method 中使用泛型#

以下代碼我們可以看到,在 struct、trait 和 impl 中都需要聲明泛型,impl 中因為存儲了算法邏輯,所以需要添加約束。

use debug::PrintTrait;

#[derive(Copy,Drop)]
struct Wallet<T> {
    balance: T
}

trait WalletTrait<T> {
    fn balance(self: @Wallet<T>) -> T;
}

impl WalletImpl<T, impl TCopy: Copy<T>> of WalletTrait<T>{
    fn balance(self: @Wallet<T>) -> T{
        *self.balance
    }
}

fn main() {
    let w = Wallet{balance:'100 000 000'};

    w.balance().print();
}

再看一個同時使用兩個不同泛型的例子:

use debug::PrintTrait;

#[derive(Copy,Drop)]
struct Wallet<T, U> {
    balance: T,
    address: U,
}

trait WalletTrait<T, U> {
    fn getAll(self: @Wallet<T, U>) -> (T, U);
}

impl WalletImpl<T, impl TCopy: Copy<T>, U, impl UCopy: Copy<U>> of WalletTrait<T, U>{
    fn getAll(self: @Wallet<T, U>) -> (T, U){
        (*self.balance,*self.address)
    }
}

fn main() {
    let mut w = Wallet{
        balance: 100,
        address: '0x0000aaaaa'
    };

    let (b,a) = w.getAll();
    b.print();
    a.print();
}

枚舉中的泛型#

剛好 Option 就是一個使用了泛型的枚舉:

enum Option<T> {
    Some: T,
    None: (),
}
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。