Skip to content

Files

Latest commit

dbb8b7f · Mar 21, 2024

History

History

23_Trait

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
Mar 21, 2024
Mar 21, 2024
Sep 12, 2023
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)。通过理解并适用这些概念,你将能够更好地进行模块化设计和代码重用,提高代码的可读性和可维护性。