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 変数を作成する(注意⚠️:作成する際には、すべてのフィールドに値を割り当てる必要があります。欠落すると、コンパイルエラーが発生します):
#[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 変数全体を変更可能にするため、個々のフィールドを個別に変更可能にすることはできません。
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 にメンバーメソッドを定義する#
ほとんどの高級プログラミング言語では、struct(またはオブジェクト)にメンバーメソッドを定義する機能がありますが、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 である必要があります
- 1 つの 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 を使用しています!)とすることができます。
1 つの 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 を同時に実装しています。