附录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中定义的数据结构提供serialize
和deserialize
函数的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,标准库还提供了 PartialOrd
和 Ord
trait,用于值排序中的比较。
PartialOrd
trait允许在一个类型的实例之间进行排序比较,从而使得我们可以使用 <、<=、> 和 >= 操作符。
当在结构体上派生 PartialOrd
时,两个实例通过依次比较每个字段来排序。