Skip to content

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。它们的关系见下表。

表达式类型
sString
&s&String
Deref::Targetstr
Deref::deref()&str
s.deref()&str
*sstr
&*s&str

以上关系有点绕。关键是要理解,*expr的类型是 Target,而deref()方法返回的类型却是&Target

标准库中有许多我们常见的类型实现了这个Deref操作符。比如Vec<T>StringBox<T>Rc<T>Arc<T>等。它们都支持“解引用”操作。从某种意义上来说,它们都可以算做特种形式的“指针”(像胖指针一样,是带有额外元数据的指针,只是元数据不限制在 usize 范围内了)。我们可以把这些类型都称为“智能指针”。

比如我们可以这样理解这几个类型:

  • Box<T>是“指针”,指向一个在堆上分配的对象;

  • Vec<T>是“指针”,指向一组同类型的顺序排列的堆上分配的对象,且携带有当前缓存空间总大小和元素个数大小的元数据;

  • String是“指针”,指向的是一个堆上分配的字节数组,其中保存的内容是合法的 utf8 字符序列。且携带有当前缓存空间总大小和字符串实际长度的元数据。

以上几个类型都对所指向的内容拥有所有权,管理着它们所指向的内存空间的分配和释放。

  • Rc<T>Arc<T>也是某种形式的、携带了额外元数据的“指针”,它们提供的是一种“共享”的所有权,当所有的引用计数指针都销毁之后,它们所指向的内存空间才会被释放。

自定义解引用操作符可以让用户自行定义各种各样的“智能指针”,完成各种各样的任务。再配合上编译器的“自动”解引用机制,非常有用。下面我们讲解什么是“自动解引用”。

Released under the MIT License