0

This code doesn't compile:

let y = {
    let x = "foo".to_owned();
    &x
};
dbg!(y);

But this code does:

let y = {
    let x = "foo".to_owned();
    &x.clone()
};
dbg!(y);

I thought we were not able to return reference to local variables, but in this case we can. x.clone() is created inside the block, but we can get a reference to it outside the block. Yet we are not able to do the same on x. What is happening here?

TSK
  • 509
  • 1
  • 9
  • @Enethecommentflagger This is not related, there is static promotion and here is temporary lifetime extension. – Chayim Friedman Jan 04 '23 at 11:37
  • @ChayimFriedman Then [this one](https://stackoverflow.com/q/47662253) and [this one](https://stackoverflow.com/questions/71719617/why-do-these-ways-of-creating-a-reference-behave-differently/71719720#71719720) would have done the trick. But now that the question was reopened, I cannot edit them in. – E_net4 Jan 04 '23 at 11:40
  • @E_net4thecommentflagger I hesitate to close this as duplicate of them as they don't clearly talk about the difference between the two snippets, and this is what this question asks. – Chayim Friedman Jan 04 '23 at 11:42
  • My understanding of Rust is limited, but I don't see the second code returning reference to a local variable. It returns a reference to a new value *intialized from* the value of `x`, not a reference to `x` itself. – chepner Jan 04 '23 at 14:08

1 Answers1

1

The rules for temporary lifetime extension are a little complex, but the difference in the 2 code snippets in this question boils down to this:

Only values created in the final expression of the block will have lifetime extension applied.

  • In the case of the 2nd block (which compiles), the return value of clone() has its lifetime extended because it was created in the final expression of the block.

  • On the other hand, in the 1st block (which fails to compile), the final expression of the initializer block is a reference to a variable created in a previous statement. This means temporary lifetime extension is not applied. In fact, when you look at it this way, it makes sense, since x is not a temporary, but an actual named variable.

It's explained in the Rust reference this way:

For a let statement with an initializer, an extending expression is an expression which is ... The final expression of any extending block expression.
...
The operand of any extending borrow expression has its temporary scope extended.

-- Rust Reference sec 10.8, Temporary Lifetime Extension

See also:

(Code in Rust Playground)

Colin D Bennett
  • 11,294
  • 5
  • 49
  • 66