Rust 学习笔记(26)-Deref trait

参考章节《Rust 程序设计语言》第15.2章 通过 Deref trait 将智能指针当作常规引用处理

我们可以像使用常规引用一样使用 Box<T>,请看如下代码

1
2
3
4
5
6
fn main() {
    let x = 5;
    let y = Box::new(x);

    assert_eq!(5, *y); // 对 y 做解引用, 我们可以像使用常规引用一样使用 Box<T>, Rust 底层实际做了 *(y.deref()) 操作
}

在之前的章节中我们说,智能指针就是一个结构体,那为什么我们可以对结构体* 解引用运算呢?答案就是 Deref trait

实现 Deref trait 允许我们重载 解引用运算符 *。通过这种方式实现 Deref trait 的智能指针可以被当作常规引用来对待,可以编写操作引用的代码并用于智能指针。

我们自己实现一个 Deref trait 来看看它的原理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
use std::ops::Deref;

struct MyBox<T>(T); // 定义一个元祖结构体

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

// 实现 Deref
impl<T> Deref for MyBox<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.0 // .0 用来访问元组结构体的第一个元素。
    }
}

deref 方法返回值的引用,以及 *(y.deref()) 括号外边的普通解引用仍为必须的原因在于所有权
如果 deref 方法直接返回值而不是值的引用,其值(的所有权)将被移出 self
在这里以及大部分使用解引用运算符的情况下我们并不希望获取内部值的所有权

Deref 强制转换

这是一个理论知识,了解一下对你有好处

Deref 强制转换(deref coercions)是 Rust 在函数或方法传参上的一种便利。
Deref 强制转换只能作用于实现了 Deref trait 的类型。
Deref 强制转换将这样一个类型的引用转换为另一个类型的引用。

让我们来看看下面这个例子

1
2
3
4
5
6
7
8
9
// 接收一个 `&str` 字符串 `slice` 参数 `name`,并打印
fn hello(name: &str) {
    println!("Hello, {}!", name);
}

fn main() {
    let m = MyBox::new(String::from("Rust")); // 我们给MyBox放入一个String类型的数据
    hello(&m); // 传递MyBox的引用进去,问题来了,为什么可以这么做?
}

我们来看看上面这个代码

  1. 首先我们知道,MyBox<T> 实现了 Deref trait,因此它可以通过 deref() 方法解引用成 T 类型,这一点没有疑问吧?
  2. 此时 String&str 还是不匹配,但为什么可以呢?原因显而易见,因为 String 也实现了 Deref trait 它解引用后返回 &str

这一切都是自动发生的,你不需要关心

updatedupdated2024-10-012024-10-01