StarknetAstro

StarknetAstro

17_CairoのTrait

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 つのメソッドのシグネチャが含まれています:newboundary、および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 を定義しました。これは、RectangleCircleの 2 つの構造体によって実装され、main 関数内で同じ名前のメンバー方法を使用しています。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。