Skip to content

16.2 Deref coercion 与自动解引用

Deref coercion

在 Rust 中,如果类型 T 实现了 Deref trait,并指定了目标类型 U: 当 T: Deref<Target=U> 时(会从 &mut T&T 转换到 &U), 当 T: DerefMut<Target=U> 时(会从 &mut T 转换到 &mut U),那么 Rust 编译器会在执行 *v 操作时,自动先将 v 进行引用归一化操作,即转换为内部通用引用的形式 &v。然后,由于 T 实现了 Deref trait,编译器会自动将 &v(类型为 &T)转换为 &U。这就是 “Deref coercion” 的过程。

假设我们有一个结构体 MyBox,它封装了一个 String,并且实现了 Deref<Target = str> trait。这样,当我们尝试将 &MyBox 作为 &str 使用时,Rust 编译器会自动使用 Deref coercion。

rust
use std::ops::Deref;

struct MyBox<T>(T);

impl<T> MyBox<T> {
    fn new(x: T) -> MyBox<T> {
        MyBox(x)
    }
}

impl<T> Deref for MyBox<T> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
      &self.0
    }
}

fn hello(name: &str) {
    println!("Hello, {name}!");
}

fn main() {
    let m = MyBox::new(String::from("Rust"));

    // 这里会自动使用 Deref coercion 将 &m 转换为 &str
    hello(&m); // 输出: Hello, Rust!
}

在这个例子中,hello 函数期望一个 &str 参数。当我们传入 &m(它是一个 MyBox 类型)时,Rust 编译器自动将 &m 转换为 &str,因为我们为 MyBox 实现了 Deref<Target = str> trait。

Deref coercion 是一种特性,它允许自动进行类型转换,将实现了Deref trait 的类型转换为目标类型的引用。 通过解引用转换,我们可以更方便地使用不同类型的智能指针和引用,而无需显式地调用 deref 方法或进行类型转换。 例如,如果你有一个类型为 Box<String> 的变量,并且你想要调用一个接受 &str 参数的方法,解引用转换会自动将 Box<String> 转换为 &String,然后再通过 String 的 Deref 实现将其转换为 &str

Rust 将分析类型并根据需要可能会多次使用 Deref::deref 以获得与参数类型匹配的引用,这些都会在编译时被确定,因此利用 Deref coercion 不会产生运行时开销!

Rust 还会强制将可变引用转换为不可变引用(当 T: Deref<Target=U> 时,从 &mut T&U),这不会违反借用规则。但是反过来的话,由于不能保证因此当前没有不可变引用,因此不能将不可变引用转换为可变引用

自动解引用

Rust 提供的“自动解引用(Automatic Dereferencing)”机制,是在某些场景下“隐式地”、“自动地”帮我们做了一些事情。什么是自动解引用呢?下面用一个示例来说明:

rust
fn main() {
    let s = "hello";
    println!("length: {}", s.len());
    println!("length: {}", (&s).len());
    println!("length: {}", (&&&&&&&&&&&&&s).len());
}

编译,成功。查文档我们可以知道,len() 这个方法的签名是:

rust
fn len(&self) -> usize

它接受的 receiver 参数是 &str,因此我们可以用 UFCS 语法调用:

rust
println!("length: {}", str::len(&s));

但是,如果我们使用 &&&&&&&&&&str 类型来调用成员方法,也是可以的。原因就是,Rust 编译器帮我们做了隐式的deref调用,当它找不到这个成员方法的时候,会自动尝试使用deref方法后再找该方法,一直循环下去。

编译器在 &&&str 类型里面找不到 len 方法;尝试将它deref,变成 &&str 类型后再寻找 len 方法,还是没找到;继续 deref,变成 &str,现在找到 len方法了,于是就调用这个方法。

自动 deref 的规则是,如果类型 T 可以解引用为 U,即 T:Deref<U>,则 &T 可以转为 &U

自动解引用(Automatic Dereferencing)是指编译器在某些情况下自动为我们解引用一个引用。例如,当我们使用一个引用调用一个方法或访问一个字段时,编译器会自动为我们解引用该引用。 这是因为方法和字段都是使用 . 运算符来访问的,而不是 * 运算符。 所以,编译器会隐含地调用 deref 方法(如果已定义)来获取指向的实际值。

需要注意的是,这里的“自动解引用”更侧重于编译器在语法糖(syntactic sugar)层面上的行为,而 Deref coercion 则更侧重于类型系统层面的自动转换。

Released under the MIT License