6.3 特征 (Traits)

声明特征 (Declaring a Trait)

A trait opts a type into a certain type of behavior or functionality that can be shared among types. This allows for easy reuse of code and generic programming. If you have ever used a typeclass in Haskell, a trait in Rust, or even an interface in Java, these are similar concepts.

特征 trait 将类型选择为可以在类型之间共享的特定类型行为或功能。这使得复用代码和通用编程变得轻松。如果您曾经在 Haskell 中使用过类型类,在 Rust 中使用过特征,甚至在 Java 中使用过接口,那么这些都是类似的概念。

Let's take a look at some code: 让我们看一些代码:

trait Compare {
    fn equals(self, b: Self) -> bool;
} {
    fn not_equals(self, b: Self) -> bool {
        !self.equals(b)
    }
}

We have just declared a trait called Compare. After the name of the trait, there are two blocks of code (a block is code enclosed in { curly brackets }). The first block is the interface surface. The second block is the methods provided by the trait. If a type can provide the methods in the interface surface, then it gets access to the methods in the trait for free! What the above trait is saying is: if you can determine if two values are equal, then for free, you can determine that they are not equal. Note that trait methods have access to the methods defined in the interface surface.

我们刚刚声明了一个名为 Compare 的特征。在特征名称之后,有两个 区块 代码(一个 是包含在{ 大括号}中的代码)。第一个块是 界面表面。第二个块是特征提供的 方法。如果一个类型可以提供接口表面中的方法,那么它就可以免费访问特征中的方法!上面的特征是说:如果你可以确定两个值是否相等,那么你可以免费确定它们不相等。请注意,特征方法可以访问接口表面中定义的方法。

实现特征 (Implementing a Trait)

Ok, so I know that numbers can be equal. I want to implement my Compare trait for u64. Let's take a look at how that is done:

好的,所以我知道数字可以相等。我想为 u64 实现我的 Compare 特性。让我们来看看这是如何完成的:

impl Compare for u64 {
    fn equals(self, b: Self) -> bool {
        self == b
    }
}

The above snippet declares all of the methods in the trait Compare for the type u64. Now, we have access to both the equals and not_equals methods for u64, as long as the trait Compare is in scope.

上面的代码片段声明了 u64 类型的特征 Compare 中的所有方法。现在,只要特征 Compare 在范围内,我们就可以访问 u64equalsnot_equals 方法。

超特征 (Supertraits)

When using multiple traits, scenarios often come up where one trait may require functionality from another trait. This is where supertraits come in as they allow you to require a trait when implementing another trait (ie. a trait with a trait). A good example of this is the Ord trait of the core library of Sway. The Ord trait requires the Eq trait, so Eq is kept as a separate trait as one may decide to implement Eq without implementing other parts of the Ord trait.

当使用多个特征时,经常会出现一个特征可能需要另一个特征功能的情况。这就是超特征的用武之地,因为它们允许您在实现另一个特性时需要一个特性(即具有特性的特性)。一个很好的例子是 Sway 核心库的Ord特征。 Ord 特性需要Eq 特性,因此Eq 保留为一个单独的特性,因为人们可能决定在不实现Ord 特性的其他部分的情况下实现Eq


trait Eq {
    fn equals(self, b: Self) -> bool;
}

trait Ord: Eq {
    fn gte(self, b: Self) -> bool;
}

impl Ord for u64 {
    fn gte(self, b: Self) -> bool {
        // As `Eq` is a supertrait of `Ord`, `Ord` can access the equals method
        self.equals(b) || self.gt(b)
    }
}

To require a supertrait, add a : after the trait name and then list the traits you would like to require and separate them with a +.

若需要超级特征,请在特征名称后添加一个:,然后列出您想要需要的特征并用+将它们分开。

ABI 超特征 (ABI supertraits)

ABIs can also have supertrait annotations:

ABI 也可以有超特征注解:

contract;

trait ABIsupertrait {
    fn foo();
}

abi MyAbi : ABIsupertrait {
    fn bar();
} {
    fn baz() {
        Self::foo() // supertrait method usage
    }
}

impl ABIsupertrait for Contract {
    fn foo() {}
}

// The implementation of MyAbi for Contract must also implement ABIsupertrait
impl MyAbi for Contract {
    fn bar() {
        Self::foo() // supertrait method usage
    }
}

The implementation of MyAbi for Contract must also implement the ABIsupertrait trait. Methods in ABIsupertrait are not available externally, i.e. they're not actually contract methods, but they can be used in the actual contract methods, as shown in the example above.

ContractMyAbi 的实现也必须实现 ABIsupertrait 特征。 ABIsupertrait 中的方法在外部不可用,即它们实际上不是契约方法,但它们可以在实际契约方法中使用,如上例所示。

ABI supertraits are intended to make contract implementations compositional, allowing combining orthogonal contract features using, for instance, libraries.

ABI 超特征旨在使合约实现组合,允许使用库来组合正交合约功能。

相关条目 (Associated Items)

Traits can declare different kinds of associated items in their interface surface: 特征可以在其接口表面声明不同种类的关联项:

关联函数(Associated functions)

Associated functions in traits consist of just function signatures. This indicates that each implementation of the trait for a given type must define all the trait functions.

特征中的关联函数仅由函数签名组成。这表明给定类型的特征的每个实现,都必须定义所有特征函数。

trait Trait {
    fn associated_fn(self, b: Self) -> bool;
}

关联常数(Associated constants)

Associated constants are constants associated with a type.

关联常数是与类型关联的常数。

trait Trait {
    const ID: u32 = 0;
}

The initializer expression of an associated constants in a trait definition may be omitted to indicate that each implementation of the trait for a given type must specify an initializer:

特征定义中关联常数 的初始化表达式可以省略,进而指示每个实现给定类型的 trait 必须指定一个初始值设定项:

trait Trait {
    const ID: u32;
}

Check the associated consts section on constants page.

检查常数 页面上的关联常数部分。

用例 (Use Cases)

自定义类型(结构、枚举)- Custom Types (structs, enums)

Often, libraries and APIs have interfaces that are abstracted over a type that implements a certain trait. It is up to the consumer of the interface to implement that trait for the type they wish to use with the interface. For example, let's take a look at a trait and an interface built off of it.

通常,库和 API 具有在实现特定特征类型上抽象的接口。由接口的使用者为其希望与接口一起使用的类型来实现该特征。例如,让我们看一下一个特征和基于其构建的接口。

library;

pub enum Suit {
    Hearts: (),
    Diamonds: (),
    Clubs: (),
    Spades: (),
}

pub trait Card {
    fn suit(self) -> Suit;
    fn value(self) -> u8;
}

fn play_game_with_deck<T>(a: Vec<T>) where T: Card {
    // insert some creative card game here
}

Note Trait constraints (i.e. using the where keyword) have not yet been implemented 注意 特征约束(即使用 where 关键字)尚未实施

Now, if you want to use the function play_game_with_deck with your struct, you must implement Card for your struct. Note that the following code example assumes a dependency games has been included in the Forc.toml file.

现在,如果你想在你的结构中使用函数play_game_with_deck,您必须为您的结构实现Card。请注意,以下代码示例假定依赖项 games 已包含在 Forc.toml 文件中。

script;

use games::*;

struct MyCard {
    suit: Suit,
    value: u8
}

impl Card for MyCard {
    fn suit(self) -> Suit {
        self.suit
    }
    fn value(self) -> u8 {
        self.value
    }
}

fn main() {
    let mut i = 52;
    let mut deck: Vec<MyCard> = Vec::with_capacity(50);
    while i > 0 {
        i = i - 1;
        deck.push(MyCard { suit: generate_random_suit(), value: i % 4}
    }
    play_game_with_deck(deck);
}

fn generate_random_suit() -> Suit {
  [ ... ]
}

Last updated