Why does this code NOT fail to compile?

i was working on a neural network library to find that you can take reference of a clone-d temporary value:

fn main() {
    let v = vec![String::from("hello"),String::from("world")];
    let r: &Vec<_> = &v.clone();
    println!("{:?}",r);
}

if i understand ownership correctly, the call to clone returns a temporary value, whose reference i store in a variable. after that, the temporary value gets dropped, which makes my reference invalid. so this should not compile, but it does. why is that?

In some cases, ie when you take a reference to a temporary and assign it to a variable, the compiler does temporary lifetime extension. So the temporary lives for the whole scope rather than the statement. This only happens for a direct reference being immediately assigned to a let binding. If you did like &v.clone()[0] then the temporary clone will not get lifetime extended and you should get an error

4 Likes

@IndianBoy42 oh wow, "lifetime extension". never heard of that concept :slight_smile:. you learn new things everyday

1 Like

I don't think that's entirely true (anymore?). That code compiles and works fine with &v.clone()[0] or &(v.clone()[0]). Also if v is an array instead of a vector. I guess there's some wizardry in Vec (and slices) to make the compiler understand how to shrink down ownership. I had to resort to a different collection type access the element using iter().next() to finally see the expected error pop up (I switched to a different collection type for no reason here):

use std::collections::BTreeSet;

fn main() {
    let mut v = BTreeSet::new();
    v.insert(String::from("hello"));
    v.insert(String::from("world"));
    let r = &(v.clone().iter().next());
    println!("{:?}", r);
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0716]: temporary value dropped while borrowed
 --> src/main.rs:7:15
  |
7 |     let r = &(v.clone().iter().next());
  |               ^^^^^^^^^               - temporary value is freed at the end of this statement
  |               |
  |               creates a temporary which is freed while still in use
8 |     println!("{:?}", r);
  |                      - borrow later used here
  |
  = note: consider using a `let` binding to create a longer lived value
1 Like

The reference spells out the current behavior in more detail. As it notes, the behavior may change at some point.

1 Like

does that mean that this is unstable and that i should not rely on this to make something that should not be broken after some time?

I expect it's more likely worded that way to enable it to be expanded in the future. The rust team is very particular about backwards compatibility.

1 Like

ah ok, thanks for the clarification :smiley: .

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.