rust语言学习笔记Trait(十一)Deref、DerefMut(解引用)

DerefDerefMut 是用于实现‌智能指针 ‌行为的核心 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、自定义结构体实现 DerefDerefMut

  • 实现 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、最佳实践与陷阱

✅ 应该做的

  1. 仅为智能指针实现 Deref
    Rust 官方建议 Deref 应仅用于智能指针类型(如 Box, Rc, Arc, RefCell 的守卫等)。目的是让智能指针"透明",让用户感觉像是在操作内部数据。
  2. 利用 Deref 提供切片视图 ‌:
    例如 Vec<T> 实现 Deref<Target = [T]>String 实现 Deref<Target = str>。这使得你可以直接在这些集合上使用切片的方法。

❌ 不应该做的

  1. 不要为了便利而滥用 Deref
    例如,不要为一个 User 结构体实现 Deref<Target = String> 以便直接调用字符串方法。这会隐藏数据的真实结构,导致代码难以阅读和维护。如果需要转换,请实现 AsRef<str> 或提供显式方法。
  2. Deref 不应失败
    deref() 方法签名返回的是引用 &Target,而不是 ResultOption。这意味着解引用操作必须总是成功。如果解引用可能失败(例如网络请求未完成),不应使用 Deref
  3. 避免循环 Deref
    确保 Target 类型不会间接地又解引用回原类型,否则会导致编译器无限递归或栈溢出。
相关推荐
留白_26 分钟前
【tableau入门学习】6、盒须图、靶心图、四象限图、甘特图、直方图
学习·甘特图
Source.Liu30 分钟前
Tauri 2.0 + Alpine.js 起步笔记(零构建方案)
rust
MSTcheng.33 分钟前
【Linux学习】Linux学习第四弹——Linux基本指令3
linux·windows·学习·操作系统·指令
lihaihui199135 分钟前
STM32 学习
学习
小灰灰搞电子44 分钟前
Rust+Slint 实现抽屉式侧边栏源码分享
开发语言·rust·侧边栏
Jul1en_1 小时前
Matt 与 Uncle Bob 的播客访谈有感
开发语言·经验分享·笔记·ai·开源·github·ai编程
是隼人1 小时前
buuctf-pwn jarvisoj_level5(64位ret2libc)题解(学习过程持续更新)
c语言·学习·安全·pwn入门·ctf入门
驭渊的小故事1 小时前
算法训练笔记:添加逗号、跳台阶、扑克牌顺子
笔记
码艺-Alimjan2 小时前
Tauri 2.x + Vue 3 桌面应用开发实战:从踩坑到完美落地
前端·javascript·vue.js·rust·typescript·go
噜~噜~噜~2 小时前
操作系统笔记-3.1.1.3 进程的内存映像
笔记·操作系统