Deref 和 DerefMut 是用于实现智能指针 行为的核心 Trait。它们允许自定义类型像普通引用一样工作,支持解引用操作符 *。
1、什么是 Deref
std::ops::Deref Trait 允许你重载解引用操作符 *。
- 普通引用 :
&T指向内存中的值,使用*可以获取该值。 - 智能指针 :如
Box<T>,Rc<T>,Arc<T>,它们是结构体,但实现了Deref,因此可以像&T一样被解引用。
(1)Deref 的定义
rust
pub trait Deref {
// 关联类型:解引用后得到的目标类型
type Target: ?Sized;
// 核心方法:返回对内部数据的不可变引用
fn deref(&self) -> &Self::Target;
}
(2)DerefMut (可变解引用)的定义
rust
pub trait DerefMut: Deref {
fn deref_mut(&mut self) -> &mut Self::Target;
}
2、自定义结构体实现 Deref 和 DerefMut
- 实现
DerefMut,必须首先实现Deref。
rust
use std::ops::{Deref, DerefMut};
// 自定义结构体
#[derive(Debug)]
struct MyBox<T>(T);
// 实现 deref 不可变引用
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
// 实现 deref_mut 可变引用,必须先实现 deref
impl<T> DerefMut for MyBox<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
fn main() {
let mut ms = MyBox(8);
*ms += 5; // 自动调用可变引用
println!("{:?}", *ms); // 自动调用不可变引用
*(ms.deref_mut()) -= 3; // 手动调用可变引用
println!("{:?}", *(ms.deref())); // 手动调用不可变引用
}
-
*ms += 5,编译器查找DerefMut实现。可变引用转换过程:
*ms->*(ms.deref_mut())->*(&mut T)->mut T -
println!("{:?}", *ms);,编译器查找Deref实现。不可变引用转换过程:
*ms->*(ms.deref())->*(&T)->T
3、最佳实践与陷阱
✅ 应该做的
- 仅为智能指针实现
Deref:
Rust 官方建议Deref应仅用于智能指针类型(如Box,Rc,Arc,RefCell的守卫等)。目的是让智能指针"透明",让用户感觉像是在操作内部数据。 - 利用
Deref提供切片视图 :
例如Vec<T>实现Deref<Target = [T]>,String实现Deref<Target = str>。这使得你可以直接在这些集合上使用切片的方法。
❌ 不应该做的
- 不要为了便利而滥用
Deref:
例如,不要为一个User结构体实现Deref<Target = String>以便直接调用字符串方法。这会隐藏数据的真实结构,导致代码难以阅读和维护。如果需要转换,请实现AsRef<str>或提供显式方法。 Deref不应失败 :
deref()方法签名返回的是引用&Target,而不是Result或Option。这意味着解引用操作必须总是成功。如果解引用可能失败(例如网络请求未完成),不应使用Deref。- 避免循环
Deref:
确保Target类型不会间接地又解引用回原类型,否则会导致编译器无限递归或栈溢出。