附录C: 可派生的 Trait

在本书的各个部分中,我们讨论了可应用于结构体和枚举定义的 derive 属性。derive 属性会在使用 derive 语法标记的类型上生成对应 trait 的默认实现的代码。

在这个附录中,我们提供了一个全面的参考,详细介绍了标准库中所有与derive属性兼容的trait。

这里列出的 trait 是仅有的在标准库中定义且能通过 derive 在类型上实现。标准库中定义的其它 trait 不能通过 derive 在类型上实现。这些 trait 不存在有意义的默认行为,所以由你负责以合理的方式实现它们。

本附录所提供的可派生 trait 列表并不全面:库可以为其自己的 trait 实现 derive,可以使用 derive 的 trait 列表事实上是无限的。

等值比较的 PartialEq 和 Eq

PartialEq trait允许在一个类型的实例之间进行等值比较,从而实现 == 和 != 运算符。

PartialEq在结构上派生时,只有当所有字段都相等时,两个实例才相等,如果任何字段不相等,实例就不相等。当在枚举上派生时,每一个成员都和其自身相等,且和其他成员都不相等。

例子:

#[derive(PartialEq, Drop)]
struct A {
    item: felt252
}

fn main() {
    let first_struct = A {
        item: 2
    };
    let second_struct = A {
        item: 2
    };
    assert(first_struct == second_struct, 'Structs are different');
}

复制值的 Clone 和 Copy

Clone trait 提供了明确创建一个值的深度拷贝的功能。

派生 Clone 实现了 clone 方法,其为整个的类型实现时,在类型的每一部分上调用了clone 方法。这意味着类型中所有字段或值也必须实现了 Clone,这样才能够派生 Clone

例子:

use clone::Clone;

#[derive(Clone, Drop)]
struct A {
    item: felt252
}

fn main() {
    let first_struct = A {
        item: 2
    };
    let second_struct = first_struct.clone();
    assert(second_struct.item == 2, 'Not equal');
}

Copy trait 允许你复制值而不需要额外的代码。你可以在任何部分都实现了Copy的类型上派生Copy

例子:

#[derive(Copy, Drop)]
struct A {
    item: felt252
}

fn main() {
    let first_struct = A {
        item: 2
    };
    let second_struct = first_struct;
    assert(second_struct.item == 2, 'Not equal');
    assert(first_struct.item == 2, 'Not Equal'); // Copy Trait prevents firs_struct from moving into second_struct
}

用Serde进行序列化

Serde为你的crate中定义的数据结构提供serializedeserialize函数的trait实现。它允许你将你的结构体转化为数组(或相反)。

例子:

use serde::Serde;
use array::ArrayTrait;

#[derive(Serde, Drop)]
struct A {
    item_one: felt252,
    item_two: felt252,
}

fn main() {
    let first_struct = A {
        item_one: 2,
        item_two: 99,
    };
    let mut output_array = ArrayTrait::new();
    let serialized = first_struct.serialize(ref output_array);
    panic(output_array);
}

输出:

Run panicked with [2 (''), 99 ('c'), ].

我们在这里可以看到,我们的结构体A已经被序列化到输出数组中。

另外,我们可以使用deserialize函数将序列化的数组转换回我们的结构体A。

例子:

use serde::Serde;
use array::ArrayTrait;
use option::OptionTrait;

#[derive(Serde, Drop)]
struct A {
    item_one: felt252,
    item_two: felt252,
}

fn main() {
    let first_struct = A {
        item_one: 2,
        item_two: 99,
    };
    let mut output_array = ArrayTrait::new();
    let mut serialized = first_struct.serialize(ref output_array);
    let mut span_array = output_array.span();
    let deserialized_struct: A = Serde::<A>::deserialize(ref span_array).unwrap();
}

这里我们要把一个序列化的数组span转换回结构体A。deserialize返回一个Option,所以我们需要把它解包。当使用deserialize时,我们还需要指定我们想要反序列化的类型。

Drop 和 Destruct

当离开作用域时,需要先移动变量。这就是 Drop trait起作用的地方。你可以在这里找到更多关于它的用法的细节。

此外,字典在离开作用域之前需要被squash(压缩)。在每个字典上手动调用squash方法很快就不再是必须操作。Destruct 特性允许字典在超出范围时被自动压缩。你也可以在这里 找到更多关于Destruct的信息。

存储

在Starknet合约中的存储变量中存储用户定义的结构体需要为该类型实现 Store trait 。您可以为所有不包含字典或数组等复杂类型的结构体自动派生 Store trait 。

例子:

#[starknet::contract]
mod contract {
    #[derive(Drop, starknet::Store)]
    struct A {
        item_one: felt252,
        item_two: felt252,
    }

    #[storage]
    struct Storage {
        my_storage: A,
    }
}

在这里,我们演示了一个派生了Store trait的struct A的实现。这个 struct A随后被用作合约中的存储变量。

用于排序比较的PartialOrd和Ord

除了 PartialEq trait,标准库还提供了 PartialOrdOrd trait,用于值排序中的比较。

PartialOrdtrait允许在一个类型的实例之间进行排序比较,从而使得我们可以使用 <、<=、> 和 >= 操作符。

当在结构体上派生 PartialOrd 时,两个实例通过依次比较每个字段来排序。

Last change: 2023-09-20, commit: cbb0049