Adding type annotation equivalent to existing type changes code semantics
This piece of code can't compile:
fn main()
{
let y = &mut 2i32;
{
let x = y;
*x = 3;
}
println!("{}", y);
}
But, if we add the type annotation, it compiles:
fn main()
{
let y = &mut 2i32;
{
let x: &mut i32 = y;
*x = 3;
}
println!("{}", y);
}
I guess the later form triggers "auto-deref" mechanism, which equals to:
let x = &mut *y;
But this behaviour is still very confusing. Type annotation or not doesn't change the type of x at all. Type of x is always &mut i32. But they lead to different compile result. I have no idea whether should we fix it.
I don't think autoderef is involved here, the type of
xstill seems to be inferred to&mut i32. Looks like some weird interaction with reborrows (a reborrow is occurring in the second case but not the first).Jonas Schievink at 2016-08-23 09:22:49
Looks like a duplicate of #25899, but I think we should keep it open anyways since this is very confusing
Jonas Schievink at 2016-08-23 09:25:03
Well, actually, this isn't really a diagnostics issue--I'm going to nominate for a lang team discussion so that we can see what we want to do here, and can we do anything here. For reference the first example compiles with the following error today:
error[E0382]: use of moved value: `y` --> test.rs:8:20 | 5 | let x = y; | - value moved here ... 8 | println!("{}", y); | ^ value used here after move | = note: move occurs because `y` has type `&mut i32`, which does not implement the `Copy` trait error: aborting due to previous errorMark Rousskov at 2017-05-12 23:10:11
The reason that this happens is that, in the second case, where the expected type is known, we will do a "reborrow coercion" (in particular, when coercing from
&mutto&mut). This means that the variable is not considered "moved". In the first case, though, the target type is not known, and hence we consider it a move. This is suboptimal but not wrong (in general, annotatingletcan induce coercions and have other changes).I think we could fix this (potentially) by detecting the case that we are coercing an
&mutto an unknown type and pre-emptively inserting a&mut *reborrow. I think this can only make errors go away, and not cause them. Probably worth trying anyway. I'd be up to mentor this.Niko Matsakis at 2017-05-25 21:29:48
Don't have time to write up detailed stuff. The basic idea is to look into the
src/librustc_typeck/check/coercion.rs.Niko Matsakis at 2017-05-25 21:31:00
Thanks for the information. It is helpful.
I tried to fix this problem, but failed. Because "always re-borrow" is not a good strategy for the time being.
libstd/io/mod.rscan not compile after this change. The case is simplified as below:let mut buf : &mut [u8] = init(); { let tmp = buf; // If we automatically add re-borrow here, below line will be failed to compile buf = &mut tmp[n..]; } // Error Message: // error[E0499]: cannot borrow `*buf` as mutable more than once at a timeI guess it could be solved when NLL is ready. Test case as below:
#![feature(nll)] // enable nll feature, and the compile error is gone fn main() { let mut buf : &mut [u8] = &mut [1,2,3]; { let tmp : &mut [u8] = &mut *buf; // enforce re-borrow occurs buf = &mut tmp[1..]; } println!("{:?}", buf); }I'll try to revisit this issue again after NLL become the default.
F001 at 2018-05-10 11:00:11
Triage: this still errors today:
Compiling playground v0.0.1 (/playground) error[E0382]: borrow of moved value: `y` --> src/main.rs:8:20 | 3 | let y = &mut 2i32; | - move occurs because `y` has type `&mut i32`, which does not implement the `Copy` trait 4 | { 5 | let x = y; | - value moved here ... 8 | println!("{}", y); | ^ value borrowed here after move error: aborting due to previous errorSteve Klabnik at 2019-10-14 12:34:44