Rust特性(Trait)
简介
Trait
(特性) 是一组方法的集合,实现trait的类型可以访问该 trait 中定义的其他方法。任何类型都可以实现 trait;
trait 是一种约束,而不是具体类型,它属于DST(类似于
str
),其 size 无法在编译阶段确定,只能通过指针来间接访问;
要点
trait
必须声明可见后才能使用;trait
本身并没有固定的大小,不能直接声明和使用trait类型的变量;某个实现该
trait
类型的实例的有效引用称为Trait Object
;所有trait都有一个隐藏的类型
Self
,代表当前实现此trait的具体类型;函数第一个参数是
self
且为Self
相关类型(Self, &Self, &mut Self, Box<Self>
),则函数为方法(method),self
称为receiver
;没有receiver参数的函数为静态方法,可通过
Type::Function()
方式调用;匿名trait无须名字,可直接在impl中实现;
可以在trait的声明中定义默认方法;
扩展trait
(extension trait), 可以为其它trait类型扩展出自定义trait的接口,impl块必须与trait或struct声明在同一个crate中(孤儿规则);继承:凡是实现了Subtrait(
Creature
)的类型, 也必须实现父Trait(Visible
)的所有方法;
孤儿规则(orphan rule)
- 如果要实现外部定义的
trait
需要先将其导入作用域; - 不允许对外部类型实现外部
trait
; - 可以对外部类型实现自定义的
trait
; - 可以对自定义类型上实现外部
trait
;
示例
|
|
trait 泛型
|
|
特征对象(trait object)
指向
trait
的指针就是Trait Object
,比如&SomeTrait
和Box<SomeTrait>
;&SomeTrait
类型和普通的指针类型不同, 不仅包括指向真实对象的指针,还包括一个指向虚函数表的指针;rust通过
TraitObject
用来实现动态分发;Trait Object
是一个胖指针fat pointer
, 占用两个机器字字节, 一个指向实际的实例对象, 一个指向虚基表vtable
;
|
|
Supertrait
关联类型
关联类型是一个将类型占位符与trait 相关联的方式,这样 trait 的方法签名中就可以使用这些占位符类型。
关联类型类似泛型,不同之处关联类型,无需标注类型,无须多次实现trait;
|
|
常用Trait
Default
Default trait是针对无参构造函数的抽象
|
|
Derive
Rust提供了一个特殊属性,以自动impl某些trait,编译阶段会自动展开为相应的impl块:
|
|
Rust支持自动derive的trait有:
Debug
Clone/Copy
Hash
PartialEq/Eq/PartialOrd/Ord
Send/Sync
Default
FromPrimitive
RustcEncodable/RustcDecodable
Display/Debug
实现
Display
特性的类型,可用{}
格式控制打印;实现
Debug
特性的类型,可用{:?},{:#?}
格式控制打印;Display
一般给最终用户显示的,通常用utf-8格式字符输出;Debug
特性主要用于调试,一般为byte字符,编译器提供自动derive功能;实现
Dispaly
特性的类型都自动实现了ToString
特性,可直接通过to_string()
格式化字符串;
|
|
派生(derive)
通过 #[derive]
属性,编译器能够提供某些 trait 的基本实现。如果 需要更复杂的行为,这些 trait 也可以手动实现。
下面是可以自动派生的 trait:
- 比较 trait:
Eq
,PartialEq
,Ord
,PartialOrd
Clone
, 用来从&T
创建副本T
。Copy
,使类型具有 “复制语义”(copy semantics)而非 “移动语义”(move semantics)。Hash
,从&T
计算哈希值(hash)。Default
, 创建数据类型的一个空实例。Debug
,使用{:?}
formatter 来格式化一个值。
|
|
Ord/PartialOrd/Eq/PartialEq
标记特性(Marker Trait)
marker trait
在std::marker
模块中定义;Marker Trait
不包含任何方法;Marker Trait
主要用于编译器,标记marker trait
的类型,其相应语义在编译期得到保证;
Send和Sync
Sync
和 Send
主要用于线程间数据同步作用的;
Send
Send
是一个marker trait
;Send
允许在线程间转移所有权;标记为
Send
类型的数据能安全地被move到另一个线程;几乎所有的 Rust 类型都是
Send
的, 裸指针除外;任何完全由
Send
的类型组成的类型也会自动被标记为Send
;Rc<T>
没有实现send
trait,无法用于线程共享,需用Arc<T>
;
Sync
Sync
是一个marker trait
;Sync
允许在线程间共享所有权;标记为
Sync
的类型可以在线程间安全的共享(通过引用);基本类型是
Sync
的;完全由
Sync
的类型组成的类型也是Sync
的;若类型 T 的引用
&T
是Send
,则T
是Sync
;
Sized
Sized
是一种marker trait
,无任何方法和联合类型,无法实现;Sized
标记在编译期可确定大小的类型;Unsized
标记的是动态大小类型,在编译期无法确定其大小;Rust不能在变量里保存大小未确定(
unsized
)的值, 也不能把unsize
值作为参数.所有大小确定的类型都实现了
std::marker::Sized
trait;Sized trait只能用于参数的类型声明(及检查), 例如
T: Sized
不能用于其它用途;?Sized
为不定大小类型 questionably sized, 可以是确定大小, 也可以是非确定大小类型.struct的最后一个字段可以是
?Sized
类型, 但如果这样, struct本身就变为了unsized.但如果写成泛型, 并传入一个Sized类型, 那么这个类型的struct仍然是Sized. 大小取决于泛型的参数类型:
|
|
Copy/Clone
Copy
Copy是一个
marker trait
,定义为std::marker::Copy
;标记为
Copy
的类型可以通过内存copy实现该类型的副本;标记为
Copy
的类型,在变量绑定、函数参数传递、函数返回值传递等场景下,不是move语义,是copy语义;只有所有的成员都实现了
Copy
,这个类型才有资格实现 Copy trait;常见的数字类型、bool类型、共享借用指针&,都是具有
Copy
属性的类型;Box、Vec、&mut 等类型不具备 Copy 属性的类型;
数组,元组,struct,enum这些组合类型当其每一个成员都是Copy类型是,其自身自动Copy;
Clone
Clone
trait不是marker trait
;Clone
是Sized
的sub-trait, 所以Self类型必须是Sized;clone
方法必须返回和self
独立无关的一份拷贝;如果所有的字段都实现了Clone, 那么
struct
可以加上属性:#[derive(Clone)]
自动实现Clone trait;
通常情况下clone的成本比较高, 但是对于
Rc<T>
和Arc<T>
这类的类型, Rust的对它们的clone只是简单的增加计数.通常尽可能使用
clone_from
来减少clone开销, 这会允许一些优化. 例如, String的clone, 被赋值的String如果capacity够大, 可以不需要释放内存, 直接把源的内容拷贝过来.如果所有的字段都实现了Clone, 那么
struct
可以加上属性:#[derive(Clone)]
自动实现Clone traitclone
方法不能失败(infallible), 对于std::fs::File
这样的类型, 有try_clone
方法, 返回std::io::Result<File>
|
|
From/Into
From
: 对于类型为U
的对象foo
,如果它实现了From<T>
,那么,可以通过let foo = U::from(bar)
来生成自己
|
|
|
|