Appearance
16.1 自定义解引用
解引用操作可以被自定义。方法是,实现标准库中的std::ops::Deref或者std::ops::DerefMut这两个 trait。
Deref 的定义如下所示。DerefMut 的唯一区别是返回的是 &mut 型引用都是类似的,因此不过多介绍了。
rust
pub trait Deref {
type Target: ?Sized;
fn deref(&self) -> &Self::Target;
}
pub trait DerefMut: Deref {
fn deref_mut(&mut self) -> &mut Self::Target;
}这个 trait 有一个关联类型 Target,代表解引用之后的目标类型。
比如,标准库中实现了 String 向 str 的解引用转换:
rust
impl ops::Deref for String {
type Target = str;
#[inline]
fn deref(&self) -> &str {
unsafe { str::from_utf8_unchecked(&self.vec) }
}
}请大家注意这里的类型,deref() 方法返回的类型是 &Target,而不是Target。
如果说有变量 s 的类型为 String,*s 的类型并不等于s.deref()的类型。
*s 的类型实际上是 Target,即 str。&*s的类型才是&str。
s.deref() 的类型为 &Target,即&str。它们的关系见下表。
| 表达式 | 类型 |
|---|---|
| s | String |
| &s | &String |
| Deref::Target | str |
| Deref::deref() | &str |
| s.deref() | &str |
| *s | str |
| &*s | &str |
以上关系有点绕。关键是要理解,*expr的类型是 Target,而deref()方法返回的类型却是&Target。
标准库中有许多我们常见的类型实现了这个Deref操作符。比如Vec<T>、String、Box<T>、Rc<T>、Arc<T>等。它们都支持“解引用”操作。从某种意义上来说,它们都可以算做特种形式的“指针”(像胖指针一样,是带有额外元数据的指针,只是元数据不限制在 usize 范围内了)。我们可以把这些类型都称为“智能指针”。
比如我们可以这样理解这几个类型:
Box<T>是“指针”,指向一个在堆上分配的对象;Vec<T>是“指针”,指向一组同类型的顺序排列的堆上分配的对象,且携带有当前缓存空间总大小和元素个数大小的元数据;String是“指针”,指向的是一个堆上分配的字节数组,其中保存的内容是合法的 utf8 字符序列。且携带有当前缓存空间总大小和字符串实际长度的元数据。
以上几个类型都对所指向的内容拥有所有权,管理着它们所指向的内存空间的分配和释放。
Rc<T>和Arc<T>也是某种形式的、携带了额外元数据的“指针”,它们提供的是一种“共享”的所有权,当所有的引用计数指针都销毁之后,它们所指向的内存空间才会被释放。
自定义解引用操作符可以让用户自行定义各种各样的“智能指针”,完成各种各样的任务。再配合上编译器的“自动”解引用机制,非常有用。下面我们讲解什么是“自动解引用”。