Rust中的Deref
常规解引用
- Deref 可以让智能指针像引用那样工作,这样你就可以写出同时支持智能指针和引用的代码,例如 T,即*智能指针(Smart Pointer)自动解引用
- Drop 允许你指定智能指针超出作用域后自动执行的代码,例如做一些数据清除等收尾工作,即方法自动调用(自动解引用 + 方法查找),注意自动解引用只适用在:函数传参和方法调用,而变量赋值时需要手动解引用
在讲智能指针解引用前,我们先来看看一般的解引用。引用是对一个变量值的内存地址的引用,解引用那就是通过内存地址获取到值。y 就是一个常规引用,包含了值 5 所在的内存地址,然后通过解引用 *y,我们获取到了值 5。1
2
3
4
5
6
7fn main() {
let x = 5;
let y = &x;
assert_eq!(5, x);
assert_eq!(5, *y);
}
智能指针解引用
当引用的是一个值的时候,我们可以直接 “*y”解引用拿到内存地址上的值,那如果引用的是一个结构体的话,直接进行解引用,编译器可能会束手无策,因此我们可以为智能指针结构体实现Deref特征。
实现 Deref 后的智能指针结构体(智能指针往往是基于结构体实现,它与我们自定义的结构体最大的区别在于它实现了 Deref 和 Drop 特征),就可以像普通引用一样,通过 * 进行解引用,例如 Box
1 | fn main() { |
智能指针 x 被 * 解引用为 i32 类型的值 1,然后再进行求和。
我们可以来定义自己的智能指针
1 | struct MyBox<T>(T); |
当解引用 MyBox 智能指针时,返回元组结构体中的元素 &self.0
“ * “ 背后的原理
当我们使用智能指针Box进行解引用的时候,实际上是执行了
1 | *(y.deref()) |
deref 方法返回值的常规引用,然后通过 * 对常规引用进行解引用,最终获取到目标值。
为什么要用这种看起来麻烦的方法,原因在于所有权系统的存在。如果 deref 方法直接返回一个值,而不是引用,那么该值的所有权将被转移给调用者,而我们不希望调用者仅仅只是 *T 一下,就拿走了智能指针中包含的值。
函数和方法中的Deref隐式转换(方法的自动调用)
对于函数的传参,rust实现了一个及其方便的隐式转换:Deref 隐式转换。若一个类型实现了Deref 特征,那它的引用在传给函数或方法时,会根据参数签名来决定是否进行隐式的 Deref 转换,例如:
1 | fn main() { |
这个有几点需要注意:
- String 实现了
Deref 特征,可以在需要时自动被转换为&str类型 - &s 是一个 &String 类型,当它被传给 display 函数时,自动通过 Deref 转换成了 &str
- 必须使用 &s 的方式来触发 Deref(仅引用类型的实参才会触发自动解引用)
连续的隐式 Deref 转换
Deref不仅仅可以进行一次隐式转换,还可以进行连续的隐式 Deref 转换,如下:
1 | fn main() { |
我们使用了之前自定义的智能指针 MyBox,并将其通过连续的隐式转换变成 &str 类型:首先 MyBox 被 Deref 成 String 类型,结果并不能满足 display 函数参数的要求,编译器发现 String 还可以继续 Deref 成 &str,最终成功的匹配了函数参数。
Deref 可变引用的转换
刚刚讲的都是不可变引用的转换,实际上Deref也存在可变引用的转换,实际上,Deref的转换规则有三个:
- 当 T: Deref<Target=U>,可以将 &T 转换成 &U,也就是我们之前看到的例子
- 当 T: DerefMut<Target=U>,可以将 &mut T 转换成 &mut U
- 当 T: Deref<Target=U>,可以将 &mut T 转换成 &U
关于DerefMut的例子:
1 | struct MyBox<T> { |
总结
一个类型为 T 的对象 foo,如果 T: Deref<Target=U>,那么,相关 foo 的引用 &foo 在应用的时候会自动转换为 &U
- Title: Rust中的Deref
- Author: tianyi
- Created at : 2025-08-06 17:28:32
- Updated at : 2025-08-09 10:35:59
- Link: https://github.com/ztygod/2025/08/06/Rust中的Deref/
- License: This work is licensed under CC BY-NC-SA 4.0.