Deref解引用
一个类型为 T 的对象 foo,如果 T: Deref<Target=U>
,那么,相关 foo 的引用&foo
在引用的时候会自动转换为&U
。
0.解引用
定义一个Deref实例
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 delete(&self) -> &Self::Target {
&self.0
}
}
fn main() {
let y = MyBox::new(5);
assert_eq!(5, *y);
}
deref
返回一个常规引用,可以被*
进行解引用- 这里其实调用了
*(y.deref())
方法进行解引用
注意:*y
到*(y.deref())
调用只会发生一次,不会递归替换(如:*((y.deref()).deref()
不会发生的)
1.函数和方法中的隐式Deref转换
函数和方法中,Rust提供一种隐式转换:Deref
转换。
fn main() {
let s = String::from("hello world");
display(&s);
}
fn display(s: &str) {
println!("{}", s);
}
- String实现了Deref特征,需要时会自动转换成
&str
类型 - 只有引用类型为参数时候,才会触发转换(
&String
->&str
)
1.1 连续的隐式Deref转换
Deref支持连续的隐式转换,直到找到合适的为止
fn main() {
let s = MyBox::new(String::from("hello world"));
display(&s)
}
fn display(s: &str) {
println!("{}",s);
}
将自定义的&MyBox
,通过连续的隐式转换成&str
类型
- 首先MyBox被Deref成String类型,但不满足display参数
- String将继续自动转换Deref成&str,匹配参数
- 连续转换在编译期间完成的,没有性能损耗。
2 引用归一化
Rust编译器实际上只能对&v
形式的引用进行解引用操作,那么问题来了,如果是一个智能指针或者 &&&&v 类型的呢? 该如何对这两个进行解引用?
答案是:Rust 会在解引用时自动把智能指针和 &&&&v 做引用归一化操作,转换成 &v 形式,最终再对 &v 进行解引用:
- 把智能指针(比如在库中定义的,Box、Rc、Arc、Cow 等)从结构体脱壳为内部的引用类型,也就是转成结构体内部的 &v
- 把多重&,例如 &&&&&&&v,归一成&v
2.1 例子
impl<T: ?Sized> Deref for &T {
type Target = T;
fn deref(&self) -> &T {
*self
}
}
- 在这段源码中,
&T
被自动解引用为T
,也就是&T: Deref<Target=T>
。 - 按照这个代码,
&&&&T
会被自动解引用为&&&T
,然后再自动解引用为&&T
,以此类推, 直到最终变成&T
。
// example 1
fn foo(s: &str) {}
// 由于String实现了Deref<Target=str>
let owned = "Hello".to_string();
// 因此下面函数可以正常运行
foo(&owned);
// example 2
use std::rc::Rc;
fn foo(s: &str) {}
// String 实现了 Deref<Target=str>
let owned = "Hello".to_string();
// 且 Rc 智能指针可以被自动脱壳为内部的 `owned` 引用: &String ,然后 &String 再自动解引用为 &str
let counted = Rc::new(owned);
// 因此下面的函数可以正常运行:
foo(&counted);
// example 3
struct Foo;
impl Foo {
fn foo(&self) { println!("Foo");}
}
let f = &&Foo;
// 下面等价
f.foo();
(&f).foo();
(&&f).foo();
(&&&&&&&&f).foo();
3.三种 Deref 转换
在之前,我们讲的都是不可变的 Deref 转换,实际上 Rust 还支持将一个可变的引用转换成另一个可变的引用以及将一个可变引用转换成不可变的引用,规则如下:
- 当 T: Deref<Target=U>,可以将 &T 转换成 &U,也就是我们之前看到的例子
- 当 T: DerefMut<Target=U>,可以将 &mut T 转换成 &mut U
- 当 T: Deref<Target=U>,可以将 &mut T 转换成 &U