-
Notifications
You must be signed in to change notification settings - Fork 13.9k
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 😇