Create chapter_3_16_3.md

This commit is contained in:
令狐一冲
2023-05-18 09:47:48 +08:00
committed by GitHub
parent a994c66582
commit bcec3e932b

View File

@@ -0,0 +1,137 @@
# 3.16.3 Deref trait
## 1. 通过*使用引用背后的值
常规引用是一个指针类型,包含了目标数据存储的内存地址。对常规引用使用 * ,可以通过解引用的方式获取到内存地址对应的数据值,示例如下:
```Rust
fn main() {
let x = 5;
let y = &x;
assert_eq!(5, x);
// assert_eq!(5, y); //编译错误,必须通过*才能使用y引用的值
assert_eq!(5, *y);
}
```
## 2. 通过*使用智能指针背后的值
对于智能指针,也可以通过*使用其背后的值,示例如下:
```Rust
fn main() {
let x = 5;
let y = Box::new(x);
assert_eq!(5, x);
assert_eq!(5, *y);
}
```
## 3. 使用Deref
实现Deref trait允许我们重载解引用运算符*。通过为类型实现Deref trait类型可以被当做常规引用来对待。简单来说如果类型A实现了Deref trait那么就可以写如下代码
```Rust
let a: A = A::new();
let b = &a;
let c = *b; //对A实现了Deref trait所以可以对A类型解引用
```
下面的代码定义一个MyBox类型并为其实现Deref trait
```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> {
//为MyBox实现Deref trait
type Target = T;
fn deref(&self) -> &T {
//注意此处返回值是引用因为一般并不希望解引用获取MyBox<T>内部值的所有权
&self.0
}
}
fn main() {
let x = 5;
let y = MyBox::new(x);
assert_eq!(5, x);
assert_eq!(5, *y); //实现Deref trait后即可解引用使用*y实际等价于 *(y.deref())
}
```
## 4. 函数和方法的隐式Deref强制转换
对于函数和方法的传参Rust 提供了隐式转换Deref 转换。若一个类型实现了 Deref 特征,那它的引用在传给函数或方法时,会根据参数签名来决定是否进行隐式的 Deref 转换,例如:
```Rust
use std::ops::Deref;
struct MyString(String);
impl Deref for MyString { // MyString类型实现了Deref trait
type Target = str;
fn deref(&self) -> &str {
self.0.as_str()
}
}
fn print(s: &str) {
println!("s: {:?}", s);
}
fn main() {
let y = MyString("Hello".to_string());
print(&y); //此处发生隐式转换,&y从&MyString自动转换为&str类型转换由&触发,过程如下:
// &y隐式转换过程: &MyString -> &str
}
```
上面代码的关键点为:
- MyString实现了Deref特征可以在需要时自动被转换为&str类型
- &y是一个&MyString类型当它被传给print函数时自动通过Deref转换成了&str
- 必须使用&y的方式来触发Deref(仅引用类型的实参才会触发自动解引用)。
下面为调用方法时发生的隐式自动转换的例子:
```Rust
use std::ops::Deref;
struct MyType(u32);
impl MyType {
fn to_u32(&self) -> u32 {
self.0
}
}
struct MyBox(MyType);
impl Deref for MyBox {
// MyString类型实现了Deref trait
type Target = MyType;
fn deref(&self) -> &MyType {
&self.0
}
}
fn main() {
let mb = MyBox(MyType(5u32));
let _a = mb.to_u32(); // 此行发生隐式转换,转换过程分析如下:
// let _a = mb.to_u32() 等价于 let _a = MyType::to_u32(&mb)
// MyType::to_u32(&mb)中,
// 1、由&触发隐式转换, &mb从&MyBox转换到&MyType
// 2、调用MyType的to_u32方法
}
```
Deref还支持连续的隐式转换示例如下
```Rust
fn print(s: &str) {
println!("{}", s);
}
fn main() {
let s = Box::new(String::from("hello world"));
print(&s) // &s隐式转换过程&Box -> &String -> &str
}
```
上面的代码中Box、String都实现了Deref当把&s传入到print函数时发送连续隐式转换&s先从&Box转换到&String再转换到&str
## 5. Deref强制转换与可变性交互
类似于如何使用Dereftrait 重载不可变引用的*运算符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。