Skip to content

特征简介

有点像 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>;
}

每天进步一丢丢