为啥在某些 trait 方法调用中会出现来自 &mut 的引用减弱?

Posted

技术标签:

【中文标题】为啥在某些 trait 方法调用中会出现来自 &mut 的引用减弱?【英文标题】:Why does reference weakening from &mut occur in some trait method calls?为什么在某些 trait 方法调用中会出现来自 &mut 的引用减弱? 【发布时间】:2018-09-02 09:18:45 【问题描述】:

Rust 中可用的少数隐式转换之一是 pointer weakening,它可以将 &mut T 转换为 &T

fn just_foo<T>(_: &T) 

just_foo(&mut vec![1, 2, 3]);

但是,匹配特征时不会发生这种情况。例如,虽然 + 运算符将引用作为右侧值实现,但它们不会接受对同一类型的可变引用:

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 trait `std::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 trait `std::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
    

才发现我可以执行&amp;Foo + &amp;mut Foo,却不行Foo + &amp;mut Foo

&Foo + &mut Foo; // ok
Foo + &mut Foo; // not ok

Full Playground

第二种情况与上面的示例一致,但第一种情况则不然。似乎 RHS &amp;mut Foo 被强制为 &amp;Foo 以匹配 &amp;Foo + &amp;Foo 的实现。看起来也没有发生其他强制,因为&amp;Foo as Add&lt;&amp;Foo&gt; 的接收类型已经是&amp;Foo。我也可以扔掉语法糖并获得相同的结果:

(&Foo).add(&mut Foo); // ok
Foo.add(&mut Foo); // not ok

鉴于根据 Nomicon 的说法,在进行特征匹配时不应该发生强制,为什么 &amp;Foo + &amp;mut Foo 不工作而 &amp;i32 + &amp;mut i32 不工作?是因为&amp;Foo 有一个Add 的实现吗?如果是这样,为什么它会使编译器的行为有所不同?

【问题讨论】:

【参考方案1】:

是不是因为 Add&amp;Foo 有一个单独的实现?

让我们看看当我们添加这个实现时会发生什么:

impl<'b> Add<Foo> for &'b Foo 
    type Output = Foo;
    fn add(self, _: Foo) -> Foo 
        Foo
    

现在&amp;Foo + &amp;mut Foo&amp;Foo + &amp;mut &amp;mut Foo 编译失败:

error[E0277]: the trait bound `&Foo: std::ops::Add<&mut Foo>` is not satisfied
  --> src/main.rs:39:10
   |
39 |     &Foo + &mut Foo;
   |          ^ no implementation for `&Foo + &mut Foo`
   |
   = help: the trait `std::ops::Add<&mut Foo>` is not implemented for `&Foo`

error[E0277]: the trait bound `&Foo: std::ops::Add<&mut &mut Foo>` is not satisfied
  --> src/main.rs:40:10
   |
40 |     &Foo + &mut &mut Foo;
   |          ^ no implementation for `&Foo + &mut &mut Foo`
   |
   = help: the trait `std::ops::Add<&mut &mut Foo>` is not implemented for `&Foo`

所以答案是是的

如果是这样,为什么它会使编译器的行为有所不同?

Add&lt;T&gt;(或任何其他通用特征)有一个适用的实现时,编译器不需要从参数推断T;它已经基于该单一实现解决了T。基本上,就好像该特征根本不是通用的。因此,也可以应用适用于非泛型参数的强制转换。

【讨论】:

以上是关于为啥在某些 trait 方法调用中会出现来自 &mut 的引用减弱?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Corona sdk 中会出现此错误?

为啥 Eclipse 中会出现重复的方法建议?

为啥 trait 中的泛型方法需要调整 trait 对象的大小?

为啥 ElasticSearch Nest Query 中会出现转义反斜杠?

为啥在 PageViewController 中会出现这种类型的滚动?

为啥在页面渲染过程中会多次调用 [ngStyle]?