StarknetAstro

StarknetAstro

11_Cairo中的struct(結構體)

11_Cairo 中的 struct (結構體)#

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

基本用法#

定義一個 struct:

#[derive(Copy, Drop)]
struct User {
    active: bool,
    username: felt252,
    email: felt252,
    sign_in_count: u64,
}

創建一個 struct 變量(注意⚠️:創建的時候,所有字段都需要賦值,漏了編譯會報error: Missing member錯誤):

#[derive(Copy, Drop)]
struct User {
    active: bool,
    username: felt252,
    email: felt252,
    sign_in_count: u64,
}

fn main() {
    let user1 = User {
        active: true, username: 'someusername123', email: '[email protected]', sign_in_count: 1
    };
}

使用 struct 變量的字段:

use debug::PrintTrait;

#[derive(Copy, Drop)]
struct User {
    active: bool,
    username: felt252,
    email: felt252,
    sign_in_count: u64,
}

fn main() {
    let user1 = User {
        active: true, 
        username: 'someusername123', 
        email: '[email protected]', 
        sign_in_count: 1
    };

    user1.active.print();
    user1.username.print();
}

修改 struct 變量字段的值。變量的可變性和不可變性同樣約束著 struct 變量,所以如果要修改 struct 變量,該變量需要是可變變量(被 mut 關鍵字修飾)。如:

use debug::PrintTrait;

#[derive(Copy, Drop)]
struct User {
    active: bool,
    username: felt252,
    email: felt252,
    sign_in_count: u64,
}

fn main() {
    let mut user1 = User {
        active: true, 
        username: 'someusername123', 
        email: '[email protected]', 
        sign_in_count: 1
    };

    user1.username = 'shalom';
    user1.username.print();
}

以上 user1 變量是用了 mut 關鍵字修飾。注意⚠️:整個 struct 變量必須同時被修飾為 mutable,不可以只將其中的某幾個字段單獨修飾為 mutable。

一種實例化 struct 變量的便捷寫法#

在很多語言中,經常會用一個函數來專門實例化一個 struct 變量,如:

use debug::PrintTrait;

#[derive(Copy, Drop)]
struct User {
    active: bool,
    username: felt252,
    email: felt252,
    sign_in_count: u64,
}

fn init_user(active: bool, username: felt252, email: felt252, sign_in_count: u64) -> User {
    User { active: active, username: username, email: email, sign_in_count: sign_in_count }
}

fn main() {
    let mut user1 = init_user(true, 'someusername123', '[email protected]', 1);

    user1.username = 'shalom';
    user1.username.print();
}

以上代碼中就使用了 init_user 函數來實例化 User struct 變量。

在這個 init_user 函數中,所有的參數和 struct 的字段名是一樣的,所以我們可以這樣簡寫:

fn init_user_simplify(active: bool, username: felt252, email: felt252, sign_in_count: u64) -> User {
	// 這裡省略了 `active:` ...
    User { active, username, email, sign_in_count }
}

在函數內部給 User 字段賦值的時候,不需要再指明字段名了,直接使用相同名的函數參數就可以。

為 struct 定義成員方法 (method)#

大多數高級編程語言中都會有給 struct(或者稱為 object)定義成員方法的特性,Cairo 也不例外。但是 Cairo 中實現這個功能需要借助Trait。如:

use debug::PrintTrait;

struct Rectangle {
    width: u32,
    high: u32
}

// 這裡聲明了一個 Trait
trait GeometryTrait {
    fn area(self: Rectangle) -> u32;
}

impl RectangleImpl of GeometryTrait {
	// 這裡實現了 Trait 中的 area 方法,與 struct 產生聯繫的就是方法中的第一個參數
    fn area(self: Rectangle) -> u32 {
        self.width * self.high
    }
}

fn main() {
    let r = Rectangle {
        width: 10,
        high: 2,
    };

    r.area().print();
}

trait 提供方法的簽名,也就是標識方法的名字、參數和返回值。impl 代碼塊中,將存放方法具體的邏輯,並且需要包含 trait 中所有方法的邏輯

注意⚠️:impl 中的代碼與 struct 產生聯繫的要求比較苛刻:

  1. 必須在方法的第一個參數指定關聯的 struct
  2. 並且參數名必須為 self
  3. 一個 impl 里 self 變量需要是同一個 struct

可以理解為:impl 是專屬於一個 struct 類型的,且必須實現 trait 中所有方法的邏輯 (trait 的意思是特徵)。

trait 中的構造函數#

trait 中,不是所有的函數都是結構體成員,它可以包含不使用 self 參數的函數,而這一類函數也通常被用作 struct 的構造函數。如:

struct Rectangle {
    width: u32,
    high: u32
}

trait RectangleTrait {
    fn square(size: u32) -> Rectangle;
}

impl RectangleImpl of RectangleTrait {
    fn square(size: u32) -> Rectangle {
        Rectangle { width: size, high: size }
    }
}

以上 square 函數就是 Rectangle 的構造函數,它裡面是沒有 self 變量的。當我們需要實例化一個 Rectangle 變量時,就可以:let square = RectangleImpl::square(10);(這裡使用的是 impl 哦!)。

一個 struct 關聯多個 trait#

struct 和 trait 之間相互獨立,同一個 struct 可以實現多個 trait。如:

struct Rectangle {
    width: u32,
    high: u32
}

struct Rectangle2 {
    width: u32,
    high: u32
}

trait RectangleCalc {
    fn area(self: Rectangle) -> u32;
}
impl RectangleCalcImpl of RectangleCalc {
    fn area(self: Rectangle) -> u32 {
        (self.width) * (self.high)
    }
}

trait RectangleCmp {
    fn can_hold(self: Rectangle, other: Rectangle) -> bool;
}

impl RectangleCmpImpl of RectangleCmp {
    fn can_hold(self: Rectangle, other: Rectangle) -> bool {
        self.width > other.width && self.high > other.high
    }
}

fn main() {}

以上代碼,Rectangle 就同時實現了 RectangleCalc 和 RectangleCmp 兩個 trait。

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