特征简介
有点像 TS 的 interface 接口,一般用于定义一个结构体具有的方法或实现。像下面这样的写法就是 Rust 的特征
rust
trait Weather {
fn get_today_weather() {}
}
常用 Trait
- Clone / Copy trait,约定了数据被深拷贝和浅拷贝的行为;
- Read / Write trait,约定了对 I/O 读写的行为;
- Iterator,约定了迭代器的行为;
- Debug,约定了数据如何被以 debug 的方式显示出来的行为;
- Default,约定数据类型的缺省值如何产生的行为;
- From / TryFrom,约定了数据间如何转换的行为。
标记 Ttrait
Sized
Sized - 用于标记有具体大小的类型。在使用泛型参数时,Rust 编译器会自动为泛型参数加上 Sized 约束。在少数情况下,需要 T 是可变类型的,Rust 提供了 ?Sized 来摆脱这个约束。
rust
struct Data<T> {
inner: T,
}
fn process_data<T>(data: Data<T>) {
todo!();
}
// 等价于 👇
struct Data<T: Sized> {
inner: T,
}
fn process_data<T: Sized>(data: Data<T>) {
todo!();
}
Send/Sync
Send/Sync 是 Rust 并发安全的基础:
- 如果一个类型 T 实现了 Send trait,所有权可以在线程间移动。
- 如果一个类型 T 实现了 Sync trait, &T 可以安全地在多个线程中共享。一个类型 T 实现了 Sync trait,则 &T 肯定也满足 Send trait。
- 如果一个类型 T: Send,那么 T 在某个线程中的独占访问是线程安全的;
- 如果一个类型 T: Sync,那么 T 在线程间的只读共享是安全的。
对于自己定义的数据结构,如果其内部的所有域都实现了 Send / Sync,那么这个数据结构会被自动添加 Send / Sync 。基本上原生数据结构都支持 Send / Sync,也就是说,绝大多数自定义的数据结构都是满足 Send / Sync 的。
标准库中,不支持 Send / Sync 的数据结构主要有:
- 裸指针 *const T / *mut T。它们是不安全的,所以既不是 Send 也不是 Sync。
- UnsafeCell 不支持 Sync。也就是说,任何使用了 Cell 或者 RefCell 的数据结构不支持 Sync。
- 引用计数 Rc 不支持 Send 也不支持 Sync。所以 Rc 无法跨线程。
Pin/Unpin
trait Unpin(std::marker::Unpin),几乎每种类型都自动实现了这个 trait,PhantomPinned,编译器为async/await desugar 之后生成的impl Future
的结构体除外。与之对应的是 Pin(这是个智能指针),它内部包裹了另外一个指针P,并且只要P指针指向的内容(我们称为T)没有实现Unpin (!Unpin),则可以保证T永远不会被移动(move)
rust
pub auto trait Unpin { }
rust
#[derive(Copy, Clone)]
pub struct Pin<P> {
pointer: P,
}
关于 Pin API 的一些信息
- 如果 T: Unpin(默认实现),然后用 Pin<'a, T> ,其实现和 &'a mut T 一样,换句话来说,只要实现了 Unpin,那么你就 Pin 不住它了,给一个实现了 Unpin trait 的类型用 Pin 包裹没任何效果。
- 如果 T 实现了 !Unpin,将一个可变引用 &mut T 变成一个 Pin 住的 T 要求使用 unsafe
- 大多数标准库的类型都实现了 Unpin,PhantomPinned,编译器为 async/await desugar 之后生成的
impl Future
的结构体除外。- 在 nightly 版本,你可以通过 feature flag 增加一个 !Unpin 约束到一个类型,或者在稳定版本中,增加 std::marker::PhantomPinned 到你的类型中
- 你可以把数据 pin 到栈上或者堆上
- Pin 一个实现了 !Unpin 的数据到栈上需要使用 unsafe
- Pin 一个实现了 !Unpin 的数据到堆上直接使用 Box::pin 即可,不需要 unsafe
- 对于 Pin 住实现了 T: !Unpin 的数据,你必须保证从它开始到被 drop 调用的过程中,其在内存中不会失效或被重新初始化
操作符 Trait
Deref / DerefMut
操作符还有加减乘除等,比如 Add trait,它允许你重载加法运算符。Rust 为所有的运算符都提供了 trait,你可以为自己的类型重载某些操作符。
rust
pub trait Deref {
// 解引用出来的结果类型
type Target: ?Sized;
fn deref(&self) -> &Self::Target;
}
pub trait DerefMut: Deref {
fn deref_mut(&mut self) -> &mut Self::Target;
}
// 用于为类型提供缺省值。也可以通过 derive 宏 #[derive(Default)] 来实现,前提是类型中的每个字段都实现了Default trait
pub trait Default {
fn default() -> Self;
}
其它
Debug / Display ,用于调试和在标准输出打印的 trait
rust
pub trait Debug {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>;
}
pub trait Display {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>;
}