简单解释
Deref定义在**std::ops**中,这里的trait允许重写操作符的行为
Deref对应的是解引用运算符:*
解引用是指通过引用(指针)拿到其指向的值
例如:
fn main() {let x = 5;let y = &x;// 这里对y解引用,拿到了它指向的值:5assert_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 traitimpl<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>变为&StringRust再次调用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方法
