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>
也是某种形式的、携带了额外元数据的“指针”,它们提供的是一种“共享”的所有权,当所有的引用计数指针都销毁之后,它们所指向的内存空间才会被释放。
自定义解引用操作符可以让用户自行定义各种各样的“智能指针”,完成各种各样的任务。再配合上编译器的“自动”解引用机制,非常有用。下面我们讲解什么是“自动解引用”。