喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)
15.3.1. 函数和方法的隐式解引用转化(Deref Coercion)
隐式解引用转化(Deref Coercion)是为函数和方法提供的一种便捷特性。
它的原理是:*假如类型T
实现了Deref
trait,那么Deref Coercion可以把T
的引用转化为T
经过Deref
操作后生成的引用。
当某类型的引用传递给函数或者是方法时,但它的类型与定义的参数类型不匹配,Deref Coercion
就会自动发生。编译器会对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 deref(&self) -> &T { &self.0 }
}
这是上篇文章的代码,定义了MyBox
元组结构体(元组结构体的介绍详见 5.1. 定义并实例化struct),创建了new
函数,并为其实现了Deref
trait,所以就可以使用一般的解引用操作来处理MyBox
。
以下是增添的部分:
fn hello(name: &str) { println!("Hello, {}", name);
}
hello
这个函数接收&str
,也就是字符串切片类型,然后打印出来。
写主函数看看实际使用:
fn main(){ let m = MyBox::new(String::from("Rust")); hello(&m);
}
m
是MyBox<String>
类型,&m
就是&MyBox<String>
,而hello
函数接收的是&str
,但这么写并不会报错,这是为什么呢?
首先MyBox
已经实现了Deref
trait,所以Rust可以调用deref
方法来把&MyBox<String>
转化为&String
,这就是刚才讲的那个比较绕的规则。
到这一步还没完,&String
类型与&str
类型不同,又是怎么转换的呢?因为String
类型也实现了Deref
trait,而且它的deref
实现是返回一个字符串切片&str
类型,所以Rust会在&String
上使用deref
把&String
转化为&str
。最终这个类型就匹配了。
而如果Rust没有Deref Coercion,那么写法会是:
hello(&(*m)[..]);
- 先使用解引用符号
*
把m
从MyBox<String>
转化为String
- 加上引用符号
&
把m
从String
转化为&String
- 通过切片操作
[..]
,可以获得 String 中的完整内容的引用,并且把其值从&String
转化为&str
15.3.2. 解引用于可变性
可以使用DerefMut
trait重载可变引用的*
运算符。DerefMut
相比Deref
多了Mut
,这是指DerefMut
返回的是可变引用&mut T
,而Deref
返回的是不可变引用&T
。
在类型和trait满足下列三种情况时,Rust会执行Deref Coercion:
-
当
T:Deref<Target=U>
,允许&T
转换为&U
:
T
实现了Deref
trait,而Deref
trait下的deref
方法的返回类型是&U
,那么&T
就可以被转化为&U
。
举个例子,上文代码例的MyBox
类型就实现了Deref
trait,其deref
方法的返回值是泛型参数&T
,所以&MyBox
就可以转换为&T
。 -
当
T:DerefMut<Target=U>
,允许&mut T
转换为&mut U
。
T
实现了DerefMut
trait(DerefMut
返回的是可变引用&mut T
),而DerefMut
trait下的deref
方法的返回类型是&mut U
,那么&mut T
就可以被转化为&mut U
。 -
当
T:Deref<Target=U>
,允许&mut T
转化为&U
。
Rust可以自动地把一个可变引用转化为不可变引用,但是反过来绝对不行。因为将不可变的引用转化为可变的引用要求引用是唯一的(借用规则中有讲,详见 4.4. 引用与借用)。