17_Cairo における Trait#
この文章で使用されている Cairo コンパイラのバージョン:2.0.0-rc0。Cairo は急速に更新されているため、異なるバージョンの構文には若干の違いがあります。将来的には、記事の内容を安定版に更新する予定です。
前回までに、trait を使用した多くのコードを書いてきました。ここで、trait の使い方をまとめてみましょう。
Trait の文字通りの意味は「特性」であり、他のプログラミング言語のインターフェース(interface)に相当します。trait を使用して、一連のメソッドをこの trait が持つ特性の具体的な内容として定義できます。任意の型は、この trait 内のすべてのメソッドを実装することができ、つまりこの trait の特性を持つことになります。
基本的な使い方#
構造体が trait を実装する際には、trait 内のすべてのメソッドを実装する必要があります。そうでなければ、この trait を実装したとは見なされず、コンパイル時にエラーが発生します。例を見てみましょう:
use debug::PrintTrait;
#[derive(Copy, Drop)]
struct Rectangle {
width: u64,
high: u64
}
trait ShapeGeometry {
fn new(width: u64, high: u64) -> Rectangle;
fn boundary(self: @Rectangle) -> u64;
fn area(self: @Rectangle) -> u64;
}
// ここで3つの関数のロジックを実装
impl RectangleGeometryImpl of ShapeGeometry {
fn new(width: u64, high: u64) -> Rectangle {
Rectangle { width, high }
}
fn boundary(self: @Rectangle) -> u64 {
2 * (*self.high + *self.width)
}
fn area(self: @Rectangle) -> u64 {
*self.high * *self.width
}
}
fn main() {
// ここで直接implを使用してnewメソッドを呼び出す
let r = RectangleGeometryImpl::new(10, 20);
// ここで構造体を使用してboundaryとareaメソッドを呼び出す
r.boundary().print();
r.area().print();
// implを使用して直接areaメソッドを呼び出す
RectangleGeometryImpl::area(@r).print();
}
上記では、Rectangle という名前の構造体を定義し、その後 ShapeGeometry という名前の trait を定義しました。trait には 3 つのメソッドのシグネチャが含まれています:new
、boundary
、およびarea
。したがって、構造体 Rectangle が trait ShapeGeometry を実装するには、trait 内の 3 つの関数のロジックをすべて impl 内に記述する必要があります。
さらに、main 関数では、直接 impl を使用してメンバー関数を呼び出すこともできることがわかります。
ジェネリック trait#
上の例では、trait 内に Rectangle 型が明記されているため、この trait は Rectangle によってのみ実装できます。複数の型が同じ trait 特性を持つシナリオに遭遇した場合、ジェネリック trait を使用する必要があります。
use debug::PrintTrait;
#[derive(Copy, Drop)]
struct Rectangle {
height: u64,
width: u64,
}
#[derive(Copy, Drop)]
struct Circle {
radius: u64
}
// このジェネリックtraitは複数の構造体によって実装可能
trait ShapeGeometryTrait<T> {
fn boundary(self: T) -> u64;
fn area(self: T) -> u64;
}
// Rectangle型によって実装される
impl RectangleGeometryImpl of ShapeGeometryTrait<Rectangle> {
fn boundary(self: Rectangle) -> u64 {
2 * (self.height + self.width)
}
fn area(self: Rectangle) -> u64 {
self.height * self.width
}
}
// Circle型によって実装される
impl CircleGeometryImpl of ShapeGeometryTrait<Circle> {
fn boundary(self: Circle) -> u64 {
(2 * 314 * self.radius) / 100
}
fn area(self: Circle) -> u64 {
(314 * self.radius * self.radius) / 100
}
}
fn main() {
let rect = Rectangle { height: 5, width: 7 };
rect.area().print(); // 35
rect.boundary().print(); // 24
let circ = Circle { radius: 5 };
circ.area().print(); // 78
circ.boundary().print(); // 31
}
上記では、ShapeGeometryTrait<T>
trait を定義しました。これは、Rectangle
とCircle
の 2 つの構造体によって実装され、main 関数内で同じ名前のメンバー方法を使用しています。