简单解释
Deref
定义在**std::ops**
中,这里的trait
允许重写操作符的行为
Deref
对应的是解引用运算符:*
解引用是指通过引用(指针)拿到其指向的值
例如:
fn main() {
let x = 5;
let y = &x;
// 这里对y解引用,拿到了它指向的值:5
assert_eq!(5, *y);
}
可以为自定义结构体**T**
实现Deref<Target=**U**>
,返回内部某个成员的引用
- 一方面,相当于对外暴露了一个可以直接使用的内部变量
- 另一方面,相当于把对结构体的引用(
**&T**
)转换为了对这个内部变量的引用(**&U**
)。
定义
trait
定义:
pub trait Deref {
type Target: ?Sized;
#[must_use]
pub fn deref(&self) -> &Self::Target;
}
一些智能指针实现了Deref trait
有些智能指针实现了Deref trait
,所以可以像一般的引用(指针)一样使用解引用运算符:
例如:Box<T>
#[test]
fn ff(){
let x = Box::new(5);
assert_eq!(5,*x)
}
自定义智能指针实现Deref trait
对于自定义的结构体,也可以通过实现Deref trait
来暴露内部的值
例如:自定义类似Box
的智能指针
use std::ops::Deref;
// 定义元组结构体存放值
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x:T) -> Self {
MyBox(x)
}
}
// 为MyBox实现Deref trait
impl<T> Deref for MyBox<T>{
type Target = T;
fn deref(&self) -> &T {
// 返回元组中第一个元素的引用
&self.0
}
}
#[test]
fn ff(){
let x = MyBox::new(5);
// 解引用操作
assert_eq!(5,*x)
}
在如上代码的24行
,编译器内部实际上会做一个转换:*(y.deref())
,也就是先获取deref
中返回的引用,再对这个常规引用进行解引用
没有Deref trait
的话,编译器只会解引用 &
引用(普通指针)类型。deref
方法向编译器提供了获取任何实现了Deref trait
的类型的值。
函数或方法中的隐式Deref
强制转换
对于实现了Deref trait
的类型,在给函数或方法传参时,会自动发生一系列的解引用转换,把提供的类型转换为参数需要的类型
例如:
// 如下的hello方法需要&str类型的参数
fn hello(name: &str) {
println!("Hello, {}!", name);
}
fn main() {
// 自定义智能指针类型
let m = MyBox::new(String::from("Rust"));
// 把MyBox类型的引用传给函数
hello(&m);
}
在如上第10行
,给hello
方法提供的是**&MyBox<String>**
类型的引用,而hello
方法需要的是**&str**
类型的引用,因此会在编译时发生一系列deref
调用来转换类型:
Rust
通过deref
调用将&MyBox<String>
变为&String
Rust
再次调用deref
将&String
变为&str
Rust
提供了**DerefMut trait**
用于重载可变引用的 *
运算符。 Rust
在发现类型和trait
实现满足三种情况时会进行Deref
强制转换:
- 当
T: Deref<Target=U>
时从&T
到&U
。 - 当
T: DerefMut<Target=U
> 时从&mut T
到&mut U
。 - 当
T: Deref<Target=U>
时从&mut T
到&U
。 :从可变引用强制转换到不可变引用
使用.
运算符时发生Deref
强制转换
例如:
#[test]
fn ff(){
let x = MyBox::new("abcde");
// deref强制转换
assert_eq!(5,x.len())
}
以上第6行
,对x
使用.
操作符时会发生deref
强制转换,因此可以直接调用String
类型的len
方法