Rust 的 Deref 到底为啥这么乱呀!

它乱就乱在语法糖!

有两种 Deref 解引用方式,一种是编译器的,一种是 Deref trait

一、明确的 Deref trait

实现了 Deref trait 的类型,一个源类型,只能实现一个 Deref trait,即只能向一种目标类型解引用。

实际上就是找它身上有没有对应方法,比如 &String 到 &str

只会解引用类型 ,而不会解值类型。比如 String 是无法自动解引到 &str 的, 必须是 &String&str 解引。

php 复制代码
fn hello(&str) {};

hello(String::from("hello")); // 是不行的,它不会自动解引用
rust 复制代码
impl Deref for String {
    type Target = str; // 唯一目标类型
    fn deref(&self) -> &str { // 参数是 &String,返回值是 &str
        // 底层逻辑:根据 String 的指针和长度,组装出一个 &str
    }
}

let s = String::from("hello world");
let b = &s; // &String 类型

// 这个 starts_with 实际上是 &str 的方法,自动 deref 了
// &String -> String -> &str 这个顺序去找 starts_with 方法
b.starts_with("hello"); 

二、点号的魔法

点号 . 的魔法 (方法决议 ),这是写在编译器里的,它也涉及到 deref,按以下步骤来

  1. 如果 T 是引用(如 &U), 解引用后 *&U 得到 U,尝试匹配方法

  2. 尝试当前类型的 self, &self, &mut self,在进入下一层 Deref 之前,编译器会把当前类型这一层的所有可能性穷尽

    比如其他 trait 有没有实现这个方法

  3. 使用 Deref trait ,用目标类型,从第2步骤循环尝试

    String 被解成 str,依次尝试 str, &str, &mut str

    会循环解引么?让类型 ATargetB,同时让类型 BTarget 又是 A。可以写出来,不过过不了编译,当解引用了 64 层,编译器就会报错

例子:

  1. 自动解引用
rust 复制代码
let n:i32 = 1;
(&n).abs(); // 这里的 . 触发了 deref,源类型是 &i32,先尝试剥离 &,发现是 i32,就能找到 abs 了
  1. String 的例子
rust 复制代码
String::from("hello world").starts_with("hello"); // 这样实现了 String 到 &str 的转变

// 如下过程
receiver = String
    self      ✗  starts_with(String)
    &self     ✗  starts_with(&String)
    &mut self ✗  starts_with(&mut String)

↓ Deref

receiver = str
    self      ✗  starts_with(str)
    &self     ✓  starts_with(&str),停止查找
    &mut self   (不会再检查)
  1. 自己实现 Deref Trait. 会一级一级自动解下去,直到最后
rust 复制代码
struct InnerRef<T>(T); // 这个其实是在栈上,主要为了了解 deref,这里不关心

// 定义 deref
impl<T> std::ops::Deref for InnerRef<T> {
   type Target = T;

   // &self => self: &Self
   fn deref(&self) -> &T {
       // 参数要是 &self 的
       &self.0 // &(self.0)
   }
}

let m = InnerRef(InnerRef(InnerRef(InnerRef(1i32)))); // 这里写上 i32 类型,不然不了解最终类型是啥,到底有没有 abs() 方法

assert_eq!(****m - 1, 0); // 需要手动解引用
assert_eq!(m.abs() - 1, 0); // 这个可以直接解到 1i32
assert_eq!((&m).abs() - 1, 0); // 这个一样
  1. Box 的例子
rust 复制代码
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized, A: Allocator> Deref for Box<T, A> {
    type Target = T;

    fn deref(&self) -> &T {
        &**self // (self: &Self) => (*self: Self) => (*Self: T) => &T (目标的引用,即 &Box<T> => &T)
    }
}

什么时候触发解引用

  1. 显式使用 * 操作符:当你对引用或智能指针(如 Box, Rc)使用 * 时,只生效一次
  2. 方法调用(. 操作符),会一直 deref 下去
  3. 函数参数传递(隐式解引用强制多态,Deref Coercion):它也会一直 deref 下去
相关推荐
程序员爱钓鱼20 小时前
Rust 数组 Array 详解:定义、访问、遍历与切片
后端·rust
程序员爱钓鱼2 天前
Rust if let 与 while let 详解:简化模式匹配
前端·后端·rust
第一程序员3 天前
Rust trait 入门:把 AI 客户端抽象成可替换接口
python·rust·github
分布式存储与RustFS3 天前
RustFS Beta.10 性能解读:PUT 全面反超 MinIO,Rust 重写对象存储成了?
开发语言·后端·rust
l1t3 天前
测试用rust重写的postgresql: pgrust
开发语言·postgresql·rust
@atweiwei3 天前
Langchainrust:中LLM-as-a-Judge,用 Rust 实现一套 LLM 评估系统
开发语言·后端·ai·rust·llm·rag
程序员爱钓鱼3 天前
Rust match 模式匹配详解:比 if 更强大的条件分支
后端·rust
爱吃牛肉的大老虎3 天前
rust基础之环境搭建
java·开发语言·rust
openKylin3 天前
与全球技术演进同频,openKylin 3.0从C迈向Rust
c语言·开发语言·rust·开源·开放原子·openkylin