RUST 学习-Struct 方法调用与Deref解引用

以下学习来自 Rust 语言圣经和 Rust 程序设计语言

1.方法调用内置规则

假设 value 为 T 类型,调用 value.foo(),编译器会做些:

  1. 首先,编译器检查它是否可以直接调用 T::foo(value),称之为值方法调用
  2. 如果上一步调用无法完成(例如方法类型错误或者特征没有针对 Self 进行实现,上文提到过特征不能进行强制转换),那么编译器会尝试增加自动引用,例如会尝试以下调用: <&T>::foo(value)<&mut T>::foo(value),称之为引用方法调用
  3. 若上面两个方法依然不工作,编译器会试着解引用 T ,然后再进行尝试。这里使用了 Deref 特征 —— 若 T: Deref<Target = U> (T 可以被解引用为 U),那么编译器会使用 U 类型进行尝试,称之为解引用方法调用
  4. T 不能被解引用,且 T 是一个定长类型(在编译期类型长度是已知的),那么编译器也会尝试将 T 从定长类型转为不定长类型,例如将 [i32; 2] 转为 [i32]
  5. 若还是不行,那…没有那了,最后编译器大喊一声:汝欺我甚,不干了!

下面我们来用一个例子来解释上面的方法查找算法:

let array: Rc<Box<[T; 3]>> = ...;
let first_entry = array[0];

array 数组的底层数据套壳在 RC、BOX 等类型中,那么编译器如何使用 array[0] 这种数组原生访问语法通过重重封锁,准确的访问到数组中的第一个元素?

  1. 首先, array[0] 只是 Index 特征的语法糖:编译器会将 array[0] 转换为 array.index(0) 调用,当然在调用之前,编译器会先检查 array 是否实现了 Index 特征。
  2. 接着,编译器检查 Rc<Box<[T; 3]>> 是否有实现 Index 特征,结果是否,不仅如此,&Rc<Box<[T; 3]>>&mut Rc<Box<[T; 3]>> 也没有实现。
  3. 上面的都不能工作,编译器开始对 Rc<Box<[T; 3]>> 进行解引用,把它转变成 Box<[T; 3]>
  4. 此时继续对 Box<[T; 3]> 进行上面的操作 :Box<[T; 3]>&Box<[T; 3]>,和 &mut Box<[T; 3]> 都没有实现 Index 特征,所以编译器开始对 Box<[T; 3]> 进行解引用,然后我们得到了 [T; 3]
  5. [T; 3] 以及它的各种引用都没有实现 Index 索引(是不是很反直觉:D,在直觉中,数组都可以通过索引访问,实际上只有数组切片才可以!),它也不能再进行解引用,因此编译器只能祭出最后的大杀器:将定长转为不定长,因此 [T; 3] 被转换成 [T],也就是数组切片,它实现了 Index 特征,因此最终我们可以通过 index 方法访问到对应的元素。

通过上面的例子看到解壳的过程就是进行解引用进入到内层类型进行查找,这样持续递归,这样我们就能达到调用被套壳对象的方法了。

2.隐式 Deref 拆解

Deref 强制转换是 Rust 在函数或方法传参上的一种便利操作,只能作用于实现了 Deref trait 的类型。通过上面例子也可以看到隐式 Deref 将原始的类型转换成了一堆其他类型进行尝试方法的调用。

Rc<Box<[T; 3]>> =》 Box<[T; 3]> =》[T; 3]

  1. 自定义类型 MyBox<T> 上实现 Deref
use std::ops::Deref;

impl<T> Deref for MyBox<T> {type Target = T;
fn deref(&self) -> &Self::Target {
        &self.0
    }
}
  1. 使用 MyBox
fn hello(name: &str) {
    println!("Hello, {name}!");
}

fn main() {
    let m = MyBox::new(String::from("Rust"));
    hello(&m);
}

这里使用 &m 调用 hello 函数,其为 MyBox<String> 值的引用.

解隐用过程:

  1. 编译器会自动尝试调用 m 的 deref 方法,在 MyBox<T> 上实现了 Deref trait,调用 deref 方法后将 &MyBox<String> 变为 &String,即&m.deref()
  2. 第一次 deref 后发现类型还是没法匹配,编译器继续尝试当前类型的 deref 方法,String 实现了 Deref 并会返回字符串 slice,这样 Rust 调用 deref 方法后类型会从 &String 变为 &str,这就符合 hello 函数的定义了

备注:这些解析查找都发生在编译时,所以利用 Deref 强制转换并没有运行时损耗!

  • 7
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值