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は複数のstructによって実装されることができます
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 を定義しています。この Trait はRectangle
とCircle
の両方の構造体によって実装され、main 関数では同じ名前のメンバーメソッドが使用されています。