-
Couldn't load subscription status.
- Fork 13.9k
Description
Here are a couple of minimal example of the situation:
trait Trait {
fn b(&mut self) {}
}
struct Struct;
impl<'a> Trait for &'a mut Struct {}
#[allow(dead_code)]
fn foo(mut a: &mut Struct) {
a.b();
}
fn main() {}trait TraitA {}
trait TraitB {
fn b(&mut self) {}
}
impl<A: TraitA> TraitB for A {}
impl<'a> TraitA for &'a mut TraitA {}
#[allow(dead_code)]
fn foo(mut a: &mut TraitA) {
a.b();
}
fn main() {}Now why was mut required on a in both cases? Because otherwise, the call to a.b() won't work: error: cannot borrow immutable argument a as mutable.
The reason why this is so is that the trait implementation is on &mut T, and so that (&mut T) is an opaque type for the purposes of the trait implementation, having no special mutability as it would otherwise have. Then, when calling the b method, it will take a second borrow &mut (&mut T). But in order to do that, it not having special knowledge of the fact that &mut T is considered mutable already, it requires you to place it in a mutable slot.
The real-life case where I hit this a couple of weeks ago and where someone else in IRC hit it today is implementing additional methods on Writer or Reader and then using the trait objects:
trait ReaderUtils: Reader {
fn read_foo(&mut self) -> Foo {
unimplemented!();
}
}
impl<R: Reader> ReaderUtils for R {}
fn unfoo(mut r: &mut Reader) {
let _ = r.read_foo();
}In this case, all of a sudden read_foo is a second-class citizen, behaving differently from the standard Reader methods. If you don't use such a method, mut r will warn you about unnecessary mutability. If you do use such a method, then all of a sudden you must use mut r.
(I'm not sure if there is a real use case for implementing traits on &mut T other than trait objects, but it's possible. It's more the trait objects that I care about.)
This is not incorrect behaviour—it is consistent; it is merely surprising behaviour. Thus, if it can be improved, it would be good. I'm not sure whether or not it can be improved, because I suspect that any special detection of a &mut T implementation as mutable would just defer the problem to the next level of generic bounds.