Description
@rustbot label A-borrow-checker, C-discussion
I’ve tested across this code example when trying to understand corner-cases of borrow checker behavior:
fn test() {
let x = Box::new(1);
let y = &(&*x, 123);
let r = &y.1;
drop(x);
println!("{r}");
}
The fact that this does work in the first place is slightly cool; given that the type of y
, &'a (&'b i32, i32)
would usually come with an 'b: 'a
bound, but the rest of the code make it so that the re-borrow in r
(which could have lifetime &'a i32
) lives longer than the Box
that the inner &'b i32
comes from.
But then I noticed – slightly disappointed – that
fn test() {
let x = Box::new(1);
let y: _ = &(&*x, 123);
let r = &y.1;
drop(x);
println!("{r}");
}
no longer works! Now, I first thought… such type annotations can sometimes reasonably have significant effects on the meaning of the code, in particular some let y: &_ = …;
usually starts allowing y
to be created through a reborrow … at least for &mut T
that’s sometimes very relevant.
But here, I’m not actually providing any concrete type in the first place. So why would the added : _
still make any difference?
For a slight variation with the same effect, these two have the same distinction:
fn test() {
let x = Box::new(1);
let y_target = (&*x, 123);
let y;
y = &y_target;
let r = &y.1;
drop(x);
println!("{r}");
}
behavior:
compiles successfully
vs
fn test() {
let x = Box::new(1);
let y_target = (&*x, 123);
let y: _;
y = &y_target;
let r = &y.1;
drop(x);
println!("{r}");
}
behavior:
error[E0505]: cannot move out of `x` because it is borrowed
--> src/lib.rs:7:10
|
2 | let x = Box::new(1);
| - binding `x` declared here
3 | let y_target = (&*x, 123);
| --- borrow of `*x` occurs here
...
7 | drop(x);
| ^ move out of `x` occurs here
8 | println!("{r}");
| --- borrow later used here
|
help: consider cloning the value if the performance cost is acceptable
|
3 - let y_target = (&*x, 123);
3 + let y_target = (&x.clone(), 123);
|
It seems surprising that let y;
and let y: _;
aren’t fully equivalent; in my mind, they should simply both leave the type of y
to-be-inferred for later. [Still, I’d also suspect that “implementation details of type inference” + “interaction of type information with the borrow checker” somehow forms the answer to this.]
I’m leaving this with just C-discussion
for now; I haven’t found this reported elsewhere here on the rust-lang/rust
issue tracker (as far as the search bar & labels could tell me). If someone else deems this to be a bug and/or well-fixable, we can turn it into C-bug
as well 😇