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 中包含三個方法的簽名: 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,它同時被 Rectangle
和 Circle
兩個結構體實現,並且在 main 函數中使用相同名字的成員方法。