🧀 Deref和Deref Coercion
&String==&str???
1
2
3
4
5
6
7
8
9
10
11
fn test(v: &str)->{
println!("{}",v);
}
fn main(){
let a = String::from("hello");
assert_eq!("hello",a);
test(&a);
}
因为在Rust中,我们知道 hello 是 str 类型
那这里 assert_eq!("hello",a) 会啥可以对比呢?
test方法需要 &str 但是我们传入 &String 为啥又可以?
先说结论,这里涉及到
- deref
- deref coercion
Deref trait
deref 是 Deref trait 中唯一的方法
实现这个trait有什么用呢???**
Implementing the
Dereftrait allows you to customize the behavior of the dereference operator*
也就是,比如 let b=&a, 在操作 *b 的时候,其实会调用定义的deref() 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
struct MyBox<T>(T);
impl<T> MyBox<T>{
fn new(v: T) -> Self{
MyBox(v)
}
}
use std::ops::Deref;
impl<T> Deref for MyBox<T>{
type Target = T;
fn deref(&self) -> &Self::Target{
&self.0
}
}
fn main(){
let a = 5;
let b = MyBox::new(x);
assert_eq!(5, a);
assert_eq!(5, *b);
}
哪里调用了?
对于任何实现了 Deref 特征的类型,compiler在看到 *b 的时候,它背后其实是在做 *(b.deref())
那 b.deref() 返回的是一个引用,&Self::Target
那么*(b.deref())中的*就是正常的解引用
而deref方法之所以要返回引用,是因为不让所有权转移
assert_eq!(“hello”,a)
assert_eq! 是一个宏
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
macro_rules! assert_eq {
($left:expr, $right:expr $(,)?) => {
match (&$left, &$right) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = $crate::panicking::AssertKind::Eq;
// The reborrows below are intentional. Without them, the stack slot for the
// borrow is initialized even before the values are compared, leading to a
// noticeable slow down.
$crate::panicking::assert_failed(kind, &*left_val, &*right_val, $crate::option::Option::None);
}
}
}
};
//...
}
match 里面的通过模式匹配 解构出来的 left_val 和right_val 两个是 reference ,
因为match 的是 (&xxx,&xxx)
1
2
let a = String::from("hello");
assert_eq!("hello",a);
代入assert_eq!("hello",a) ,left_val 就是 &str, right_val就是 &String
这两个的deref都是定义在std里面
if !(*left_val == *right_val) { 这一步就是上面讨论的了
那这里一个是 &str 一个是 &String ?
因为 String 类型实现的 Deref
fn deref(&self) -> &str 它返回的是&str
Deref Coercion
Coercion: the use of force to persuade someone to do something that they are unwilling to do
这个特性也是Rust编译器会帮助我们实现的,它是在干什么呢?
Deref coercion converts a reference to a type that implements the Deref trait into a reference to another type.
就好像上面的 String 实现了 Deref trait,它返回 &str
所以
1
2
3
4
5
6
7
8
9
fn test(v: &str)->{
println!("{}",v);
}
fn main(){
let a = String::from("hello");
test(&a);
}
这里当 一个函数的参数需要 &str 但我传进来 &String的时候,编译器会进行自动转换 注意这里参数类型是 reference
不然的话,我们需要手动 * 操作,这里也会涉及到deref ,*(x.deref()),然后得到值,然后再 & 得到值的引用
编译器就帮我们做了这一步了,
A sequence of calls to the deref method converts the type we provided into the type the parameter needs.
call deref 这个过程是可以一直下去的,如果需要的话,直到可以找到参数需要的类型
重要Demo!!!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
struct MyBox<T>(T);
impl<T> MyBox<T>{
fn new(v: T) -> Self{
MyBox(v)
}
}
use std::ops::Deref;
impl<T> Deref for MyBox<T>{
type Target = T;
fn deref(&self) -> &Self::Target{
&self.0
}
}
fn hello(name: &str){
println!("hello,{name}");
}
fn main(){
let m = MyBox::new(String::from("Rust"));
hello(&m);
}
这里hello 参数需要的是 &str
但是我们传入了&MyBox<String> 类型
不过没关系,MyBox<T> 实现了 Deref
所以这里编译器可以帮我们从 &MyBox<String> 得到 &String,
而&String与目标&str 也不匹配,
不过没关系,String 实现了Deref (std实现的)
所以这里编译器可以帮我们从&String 得到 &str
从而编译通过
DerefMut
Deref trait 是在 对不可变引用 进行* 操作, 如果要对 可变引用进行 * 操作,那么需要实现 DerefMut trait
那么现在deref coercion可以处理以下三种情况:
- From &T to &U when T: Deref<Target=U>
- From &mut T to &mut U when T: DerefMut<Target=U>
- From &mut T to &U when T: Deref<Target=U>
前两个都很好理解 比较重要的是第三个,但其实这里也就是说明了一点,可以从可变转到不可变,也就是参数申明要的是 &U,但我传进来 &mut T
但不可以从不可变转到可变。这也很好理解,因为Rust需要保证任何时候最多只有一个可变引用。