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 產生聯繫的要求比較苛刻:
- 必須在方法的第一個參數指定關聯的 struct
- 並且參數名必須為 self
- 一個 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。