Return reference to a vec from code block

Hello, could someone explain plz why this compiles?

fn main() {
    let vec_ref: &Vec<i32> = {
        &vec![1, 2, 3]
    };

    dbg!(vec_ref);
 }

It looks like returning reference to temporary value

There is a (rather niche) special-casing rule called “temporary lifetime extension” that allows the scope of temporaries to be expanded to the whole block around the surrounding let-statement i.e. in tis case to the whole function body under certain conditions. Without this special-case rule, you would be right in expecting the code not to compile.


The rule is a bit unnecessary IMO, but it can, occasionally, save the need to define one extra local variable. It also (mostly) only affects the meaning of code that otherwise wouldn’t have compiled at all.

6 Likes

Now I'm curious. Is it really "mostly"? Is there any example when the runtime behavior is changed?

Thanks a lot! :+1:
I've only managed to google 'static lifetime explanations to similar code cases which does not apply here, your reply puts things in order!

Certainly. If the reference is never used, the code would also compile without temporary lifetime extension. In cases where code compiles either way, the temporary lifetime extension can affect drop order. For example

#[derive(Debug)]
struct Loud(&'static str);
impl Drop for Loud {
    fn drop(&mut self) {
        println!("dropped {self:?}");
    }
}

// identity function
fn id<T>(x: T) -> T {
    x
}

fn foo() {
    let _reference = id(&Loud("foo1")); // `id` call prevents tmp. lifet. ext.
    // _reference references a temporary; it’s immediately no longer alive,
    // but NLL allows this, as long as it’s never used
    let x = Loud("foo2");
}
fn bar() {
    let _reference = &Loud("bar1"); // triggers temporary lifetime ext.
    let x = Loud("bar2");
    // extended temporary dropped here after `x` is dropped 
}

fn main() {
    foo();
    bar();
}
dropped Loud("foo1")
dropped Loud("foo2")
dropped Loud("bar2")
dropped Loud("bar1")
2 Likes

Huh! I didn't know this extended into blocks at all. I had overgeneralized from all the times writing something like this didn't work — when the blocks in question were part of an if expression.

It'd be convenient and actually more expressive if that worked (because the temporary in each branch might have a different type even when the borrows do not).

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.