One of the few implicit conversions available in Rust is pointer weakening, which can turn a &mut T
into a &T
:
fn just_foo<T>(_: &T) {}
just_foo(&mut vec![1, 2, 3]);
However, this doesn't happen when matching traits. For instance, although the + operator with references as right-hand sided values is implemented for numeric types, they won't accept mutable references to the same type:
5 + &mut 5;
(&5) + &mut 5;
The error message:
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}`
For another, more intriguing example, I added an assortment of implementations of Add
for a unit type Foo
:
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
}
}
Only to find that I can perform &Foo + &mut Foo
, but not Foo + &mut Foo
:
&Foo + &mut Foo; // ok
Foo + &mut Foo; // not ok
Full Playground
The second case is in line with the previous example above, but the first one isn't. It seems that the RHS &mut Foo
was coerced to &Foo
to match the implementation of &Foo + &Foo
. It doesn't look either that other coercions are taking place, because the receiving type for &Foo as Add<&Foo>
is already &Foo
. I could also throw the syntactic sugar away and obtain the same outcome:
(&Foo).add(&mut Foo); // ok
Foo.add(&mut Foo); // not ok
Given that coercions, according to the Nomicon, are not supposed to happen when doing trait matching, why does this &Foo + &mut Foo
work when &i32 + &mut i32
doesn't? Is it because there is a single implementation of Add
for &Foo
? If so, why does it make the compiler behave differently?
A weak reference allows the garbage collector to collect an object while still allowing an application to access the object. If you need the object, you can still obtain a strong reference to it and prevent it from being collected.
A weakly referenced object is cleared by the Garbage Collector when it's weakly reachable. Weak reachability means that an object has neither strong nor soft references pointing to it. The object can be reached only by traversing a weak reference.
A weak reference to an object is not enough to keep the object alive: when the only remaining references to a referent are weak references, garbage collection is free to destroy the referent and reuse its memory for something else.
In computer programming, a weak reference is a reference that does not protect the referenced object from collection by a garbage collector, unlike a strong reference.
Is it because there is a single implementation of
Add
for&Foo
?
Let's see what happens when we add this implementation:
impl<'b> Add<Foo> for &'b Foo {
type Output = Foo;
fn add(self, _: Foo) -> Foo {
Foo
}
}
Now &Foo + &mut Foo
and &Foo + &mut &mut Foo
fail to compile:
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`
So the answer is yes.
If so, why does it make the compiler behave differently?
When there is a single applicable implementation of Add<T>
(or any other generic trait), the compiler doesn't need to infer T
from the arguments; it has already resolved T
based on that single implementation. Basically, it's as if the trait wasn't generic at all. Therefore, the coercions that work on non-generic arguments can be applied too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With