Appearance
16.2 Deref coercion 与自动解引用
Deref coercion
在 Rust 中,如果类型 T 实现了 Deref trait,并指定了目标类型 U: 当 T: Deref<Target=U>
时(会从 &mut T
、&T
转换到 &U
), 当 T: DerefMut<Target=U>
时(会从 &mut T
转换到 &mut U
),那么 Rust 编译器会在执行 *v
操作时,自动先将 v
进行引用归一化操作,即转换为内部通用引用的形式 &v
。然后,由于 T 实现了 Deref trait,编译器会自动将 &v
(类型为 &T
)转换为 &U
。这就是 “Deref coercion” 的过程。
假设我们有一个结构体 MyBox
,它封装了一个 String,并且实现了 Deref<Target = str>
trait。这样,当我们尝试将 &MyBox
作为 &str
使用时,Rust 编译器会自动使用 Deref coercion。
rust
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
fn hello(name: &str) {
println!("Hello, {name}!");
}
fn main() {
let m = MyBox::new(String::from("Rust"));
// 这里会自动使用 Deref coercion 将 &m 转换为 &str
hello(&m); // 输出: Hello, Rust!
}
在这个例子中,hello 函数期望一个 &str
参数。当我们传入 &m
(它是一个 MyBox 类型)时,Rust 编译器自动将 &m
转换为 &str
,因为我们为 MyBox 实现了 Deref<Target = str>
trait。
Deref coercion 是一种特性,它允许自动进行类型转换,将实现了Deref trait 的类型转换为目标类型的引用。 通过解引用转换,我们可以更方便地使用不同类型的智能指针和引用,而无需显式地调用 deref
方法或进行类型转换。 例如,如果你有一个类型为 Box<String>
的变量,并且你想要调用一个接受 &str
参数的方法,解引用转换会自动将 Box<String>
转换为 &String
,然后再通过 String 的 Deref 实现将其转换为 &str
。
Rust 将分析类型并根据需要可能会多次使用 Deref::deref
以获得与参数类型匹配的引用,这些都会在编译时被确定,因此利用 Deref coercion 不会产生运行时开销!
Rust 还会强制将可变引用转换为不可变引用(当 T: Deref<Target=U>
时,从 &mut T
到 &U
),这不会违反借用规则。但是反过来的话,由于不能保证因此当前没有不可变引用,因此不能将不可变引用转换为可变引用。
自动解引用
Rust 提供的“自动解引用(Automatic Dereferencing)”机制,是在某些场景下“隐式地”、“自动地”帮我们做了一些事情。什么是自动解引用呢?下面用一个示例来说明:
rust
fn main() {
let s = "hello";
println!("length: {}", s.len());
println!("length: {}", (&s).len());
println!("length: {}", (&&&&&&&&&&&&&s).len());
}
编译,成功。查文档我们可以知道,len()
这个方法的签名是:
rust
fn len(&self) -> usize
它接受的 receiver 参数是 &str
,因此我们可以用 UFCS 语法调用:
rust
println!("length: {}", str::len(&s));
但是,如果我们使用 &&&&&&&&&&str
类型来调用成员方法,也是可以的。原因就是,Rust 编译器帮我们做了隐式的deref
调用,当它找不到这个成员方法的时候,会自动尝试使用deref
方法后再找该方法,一直循环下去。
编译器在 &&&str
类型里面找不到 len
方法;尝试将它deref
,变成 &&str
类型后再寻找 len
方法,还是没找到;继续 deref
,变成 &str
,现在找到 len
方法了,于是就调用这个方法。
自动 deref
的规则是,如果类型 T 可以解引用为 U,即 T:Deref<U>
,则 &T
可以转为 &U
。
自动解引用(Automatic Dereferencing)是指编译器在某些情况下自动为我们解引用一个引用。例如,当我们使用一个引用调用一个方法或访问一个字段时,编译器会自动为我们解引用该引用。 这是因为方法和字段都是使用 .
运算符来访问的,而不是 *
运算符。 所以,编译器会隐含地调用 deref
方法(如果已定义)来获取指向的实际值。
需要注意的是,这里的“自动解引用”更侧重于编译器在语法糖(syntactic sugar)层面上的行为,而 Deref coercion 则更侧重于类型系统层面的自动转换。