为什么 &mut 的引用弱化发生在某些特征方法调用中?
Why does reference weakening from &mut occur in some trait method calls?
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
}
}
才发现我可以执行&Foo + &mut Foo
,但不能执行Foo + &mut Foo
:
&Foo + &mut Foo; // ok
Foo + &mut Foo; // not ok
第二种情况符合上面前面的例子,但第一种不符合。似乎 RHS &mut Foo
被强制为 &Foo
以匹配 &Foo + &Foo
的实现。看起来也没有发生其他强制转换,因为 &Foo as Add<&Foo>
的接收类型已经是 &Foo
。我也可以扔掉语法糖并获得相同的结果:
(&Foo).add(&mut Foo); // ok
Foo.add(&mut Foo); // not ok
鉴于根据 Nomicon,在进行特征匹配时不应发生强制转换,为什么 &Foo + &mut Foo
有效而 &i32 + &mut i32
无效?是因为 &Foo
有一个 Add
的实现吗?如果是这样,为什么它会使编译器表现不同?
Is it because there is a single implementation of Add
for &Foo
?
让我们看看添加此实现后会发生什么:
impl<'b> Add<Foo> for &'b Foo {
type Output = Foo;
fn add(self, _: Foo) -> Foo {
Foo
}
}
现在&Foo + &mut Foo
和&Foo + &mut &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`
所以答案是是。
If so, why does it make the compiler behave differently?
当 Add<T>
(或任何其他通用特征)只有一个适用的实现时,编译器不需要从参数中推断出 T
;它已经基于该单一实施解决了 T
。基本上,就好像这个特征根本不是通用的。因此,也可以应用适用于 non-generic 参数的强制转换。
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
}
}
才发现我可以执行&Foo + &mut Foo
,但不能执行Foo + &mut Foo
:
&Foo + &mut Foo; // ok
Foo + &mut Foo; // not ok
第二种情况符合上面前面的例子,但第一种不符合。似乎 RHS &mut Foo
被强制为 &Foo
以匹配 &Foo + &Foo
的实现。看起来也没有发生其他强制转换,因为 &Foo as Add<&Foo>
的接收类型已经是 &Foo
。我也可以扔掉语法糖并获得相同的结果:
(&Foo).add(&mut Foo); // ok
Foo.add(&mut Foo); // not ok
鉴于根据 Nomicon,在进行特征匹配时不应发生强制转换,为什么 &Foo + &mut Foo
有效而 &i32 + &mut i32
无效?是因为 &Foo
有一个 Add
的实现吗?如果是这样,为什么它会使编译器表现不同?
Is it because there is a single implementation of
Add
for&Foo
?
让我们看看添加此实现后会发生什么:
impl<'b> Add<Foo> for &'b Foo {
type Output = Foo;
fn add(self, _: Foo) -> Foo {
Foo
}
}
现在&Foo + &mut Foo
和&Foo + &mut &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`
所以答案是是。
If so, why does it make the compiler behave differently?
当 Add<T>
(或任何其他通用特征)只有一个适用的实现时,编译器不需要从参数中推断出 T
;它已经基于该单一实施解决了 T
。基本上,就好像这个特征根本不是通用的。因此,也可以应用适用于 non-generic 参数的强制转换。