Skip to content

Latest commit

 

History

History

23_Trait

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
title tags
23. 特质和实现
cairo
starknet
trait
impl
implementation

WTF Cairo极简教程: 23. 特质和实现

我最近在学cairo-lang,巩固一下细节,也写一个WTF Cairo极简教程,供小白们使用。教程基于cairo 2.2.0版本。

推特:@0xAA_Science@WTFAcademy_

WTF Academy 社群:Discord微信群官网 wtf.academy

所有代码和教程开源在 github: github.com/WTFAcademy/WTF-Cairo


在本教程中,我们将探讨 Cairo 中的特质(trait)和实现(Implementaion),让你更好地进行模块化设计和代码重用。

Trait

在 Cairo 中,Trait 是一种定义了某些行为(方法)的抽象类型。它本身不会实现任何功能,但是会指定一组函数签名,它只是定义了一种模式,或者说约定了一种行为方式。然后你可以在任何类型上实现这些 Trait,从而允许这些类型拥有与 Trait 定义的相应行为。

从某种程度上来说,Trait与接口有一些相似的地方。

下面,我们举个计算矩形几何属性的例子。首先,我们创建一个 Rectangle 结构体,它包含两个字段:高度 h 和宽度 w

// 示例结构体
#[derive(Copy, Drop)]
struct Rectangle{
    h: u64,
    w: u64,
}

然后我们创建一个叫做 GeometryFunctions 的 Trait,它包含函数 area()boundary(),分别用来计算矩阵的面积和周长,还有一个函数change_h,用来修改矩形的高。

// 我们的蓝图,trait
trait RectGeometry {
    fn boundary(self: Rectangle) -> u64;
    fn area(self: Rectangle) -> u64;
    fn change_h(ref self: Rectangle, value: u64);
}

Trait 声明以 trait 关键字开始,接着是 Trait 名称(用帕斯卡命名 PascalCase),然后在 {} 内写一组函数签名(不是实现了的函数)。

Implementation

有了 trait,我们就可以开始构建功能了。编写实现的规则:

  1. 实现中的函数参数和返回值类型必须与 trait 规范相同。
  2. trait 中的所有函数必须由实现来实现。

下面是实现 RectGeometry Trait 的例子:

// 为 `Rectangle` 类型的 trait 的实现
impl RectGeometryImpl of RectGeometry {
    fn boundary(self: Rectangle) -> u64 {
        2_u64 * (self.h + self.w)
    }

    fn area(self: Rectangle) -> u64 {
        self.h * self.w
    }

    fn change_h(ref self: Rectangle, value: u64) {
        self.h = value;
    }
}

实现以 impl 关键字开始,接着是实现的名称(RectGeometryImpl),然后是 of 关键字和正在实现的 trait 的名称(RectGeometry),以及包含在 trait 中的函数集合。

无特质声明的实现

你可以使用#[generate_trait],在不用单独声明trait的情况下直接使用impl构建功能,简化合约,此时,of关键字后面跟着合约名称:

#[generate_trait]
    impl ImplicitInterfaceContract of trait_impl {
        fn get_value(self: @ContractState) -> u32 {
            3_u32
        }
    }

调用实现中的函数

1. 通过实现名称

你可以通过实现名称来从实现中调用函数:

ImplementationName::function_name( parameter1, parameter2 );

例如:

#[external(v0)]
fn call_impl(self: @ContractState) -> (u64, u64) {
    let rect = Rectangle { h: 5_u64, w: 7_u64 };
    (RectGeometryImpl::boundary(rect), RectGeometryImpl::area(rect))
}

#[external(v0)]
fn change_height_first(self: @ContractState) -> u64 {
    let mut rect = Rectangle { h: 5_u64, w: 7_u64 };
    RectGeometryImpl::change_h(ref rect,6);
    rect.h
}

2. 通过结构体对象

当实现的函数参数使用 self 关键字时,可以直接从相应的结构体对象访问方法。在这种情况下,你不需要明确传递 self 参数值,它会自动为你提供。

obj_name.function_name( parameter );

例如:

#[external(v0)]
fn call_object(self: @ContractState) -> (u64, u64) {
    let rect = Rectangle { h: 5_u64, w: 7_u64 };
    (rect.boundary(), rect.area())
}

#[external(v0)]
fn change_height_second(self: @ContractState) -> u64 {
    let mut rect = Rectangle { h: 5_u64, w: 7_u64 };
    rect.change_h(6_u64);
    rect.h
}

3. 直接外部调用实现中的函数

当实现使用#[abi(per_item)]修饰,实现中的函数使用#[external(v0)]修饰时,实现中的函数可以直接被外部调用。

#[abi(per_item)]
#[generate_trait]
impl ImplicitInterfaceContract of trait_impl {
    #[external(v0)]
    fn get_value(self: @ContractState) -> u32 {
        3_u32
    }
}

总结

在本章中,我们探讨了 Cairo 中的特质(trait)和实现(Implementation)。通过理解并适用这些概念,你将能够更好地进行模块化设计和代码重用,提高代码的可读性和可维护性。