trait MyTrait {
fn consume(self);
}
struct S;
impl MyTrait for &S {
fn consume(self) {
println!("borrowed")
}
}
fn main() {
let s = S;
s.consume();
s.consume();
}
compiler will accept the code, which is, programmatically correct, but contradicts the intuition s.consume() would take ownership of s.
One has to know the auto-ref rule (not in official docs) as well as the existence of ``impl MyTrait for &S``` to reason about why it compiles.
How would you propose restricting auto-ref, without breaking existing code that uses this?
For any single crate, if the author later introduce impl MyTrait for S, it will break existing code anyway. This situation could be improved if the compiler reject the code in the first place.
How about adding a flag like how NLL was introduced?
Very simple steps, and if you go down step 1, and fail to compile later the compiler will not backtrack and try another route. That would be absolutely terrible for compile times.
trait MyTrait {
fn consume(self);
}
struct S;
impl MyTrait for S {
fn consume(self) {
println!("owned")
}
}
// impl MyTrait for &S {
// fn consume(self) {
// println!("borrowed")
// }
// }
fn main() {
let s = S;
let b = &s;
b.consume();
b.consume();
}
error[E0507]: cannot move out of `*b` which is behind a shared reference
--> src/main.rs:23:5
|
23 | b.consume();
| ^ move occurs because `*b` has type `S`, which does not implement the `Copy` trait
error[E0507]: cannot move out of `*b` which is behind a shared reference
--> src/main.rs:24:5
|
24 | b.consume();
| ^ move occurs because `*b` has type `S`, which does not implement the `Copy` trait
error: aborting due to 2 previous errors
Step1: There is an impl for T, so the compiler automatically changes b to *b ?
Then uncomment impl MyTrait for &S, compiler still sees: step1, there is an impl for T, where T is &S, in this case?
Oh, I forgot about dereferencing, in this case T == &S, so we don't have any of T, &mut T, or &T so we try dereferencing because we have an impl for S. In this case because S: !Copy this fails.
So updated list,
Is there an impl for T , if so use that impl, else go to step 2
Is there an impl for &T , if so use that impl, else go to step 3,
Is there an impl for &mut T , if so use that impl, else go to step 4
Is T: Deref, then try dereferencing (this may fail if <T as Deref>::Target is not Copy or of T is not Copy)
disclaimer there may be other corner cases that I missed, but this should cover the vast majority of cases.