为何Rust中部分trait方法调用会出现&mut引用弱化现象?
Rust里有个很特殊的隐式转换——指针弱化,也就是把&mut T自动转成&T,这也是Rust为数不多的隐式转换之一。比如下面这段代码完全能正常运行:
fn just_foo<T>(_: &T) {} just_foo(&mut vec![1, 2, 3]); // &mut Vec<i32>被隐式转成了&Vec<i32>
但这里有个容易踩坑的点:这种隐式转换在trait匹配的时候不会生效。
先拿大家熟悉的数值类型Add trait举例子:我们知道整数类型实现了以&T为右操作数的+运算符,但如果你传一个&mut T过去,直接就会编译报错:
5 + &mut 5; // 编译错误 (&5) + &mut 5; // 同样编译错误
对应的错误信息很直白:
error[E0277]: the trait bound
{integer}: std::ops::Add<&mut {integer}>is not satisfied
--> src/main.rs:38:7
| 38 | 5 + &mut 5;
| ^ no implementation for{integer} + &mut {integer}
| = help: the traitstd::ops::Add<&mut {integer}>is not implemented for{integer}error[E0277]: the trait bound
&{integer}: std::ops::Add<&mut {integer}>is not satisfied
--> src/main.rs:43:10
| 43 | (&5) + &mut 5;
| ^ no implementation for&{integer} + &mut {integer}
| = help: the traitstd::ops::Add<&mut {integer}>is not implemented for&{integer}
我还做了个更有意思的测试:给自定义的单元类型Foo实现了几种不同的Add版本:
use std::ops::Add; #[derive(Debug, Default)] struct Foo; impl Add<Foo> for Foo { type Output = Foo; fn add(self, _: Foo) -> Foo { Foo } } impl<'a> Add<&'a Foo> for Foo { type Output = Foo; fn add(self, _: &'a Foo) -> Foo { Foo } } impl<'a, 'b> Add<&'a Foo> for &'b Foo { type Output = Foo; fn add(self, _: &'a Foo) -> Foo { Foo } }
结果很有意思:&Foo + &mut Foo能正常编译,但Foo + &mut Foo却不行!
这背后的原因其实很好理解:
- 对于
&Foo + &mut Foo,Rust会尝试为&mut Foo寻找可隐式转换的类型,发现可以转成&Foo,而我们正好实现了Add<&Foo>给&Foo,所以匹配成功。 - 但对于
Foo + &mut Foo,我们只给Foo实现了Add<Foo>和Add<&Foo>。Rust在匹配trait Add<&mut Foo>的时候,不会自动把右操作数的&mut Foo转成&Foo来适配已有的trait实现——因为trait匹配要求类型参数精确匹配,这种隐式转换不会被自动应用在trait的类型参数上,只有在函数参数传递、赋值等场景才会触发。
如果想要让Foo + &mut Foo生效,你可以手动做转换,比如:
Foo + &*&mut Foo; // 手动把&mut Foo转成&Foo,就能正常编译了
内容的提问来源于stack exchange,提问作者E_net4

