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
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.
Now I'm curious. Is it really "mostly"? Is there any example when the runtime behavior is changed?
Thanks a lot!
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")
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.