1

Consider the following Rust function:

enum MyType {One, Two}

fn example<'a>() -> &'a MyType {
    let t = MyType::Two;
    return &t;
}

It clearly fails to compile and produces an appropriate warning, as I attempted to return a reference to a value that goes out of scope at the end of the function and consequently gets freed.

However, if I instead "merge" the two statements into one, i.e. I use the & operator right before an instance of MyType::Two, it compiles without any errors:

fn example<'a>() -> &'a MyType {
    return &(MyType::Two);
}

I do understand why the following works:

fn example() -> &'static MyType {
    return &(MyType::Two);
}

By annotating the return type of the function with the static lifetime I am telling the compiler that there will always exist an instance of MyType::Two in memory that will be valid for the entire duration of the program.


My question then is:

  • Why does the example in the second code block compile? What information does the compiler gain by me annotating the return type with the generic a lifetime that allows it to guarantee that the reference to MyType::Two will be valid by the time the function returns?

(this makes no sense to me as I have used the generic 'a lifetime parameter and not 'static)

bool3max
  • 2,748
  • 5
  • 28
  • 57
  • 3
    You are saying to the compiler that lifetime is `'a` and returning an pointer with `'static`. The compiler is fine with that 'cause "aways `'static >= 'a`" – BiagioF Jun 05 '22 at 19:56
  • 2
    The difference might be clearer if you wrote the second body as `let t = &MyType::Two; return t;` It's a question of what's being borrowed: the local `t`, or the temporary object `MyType::Two`. [Rust lets the latter be borrowed `'static`ly](https://github.com/rust-lang/rfcs/blob/master/text/1414-rvalue_static_promotion.md) – Brian61354270 Jun 05 '22 at 20:03
  • Ah, so `&MyType::Two` behind the scenes is actually of the type `&'static MyType` which lives longer than any other possible lifetime. I get it, thanks. – bool3max Jun 05 '22 at 20:22
  • This is one of the questions I hate the most since it already has a good answer but people don't vote to close it, apparently because they can't find it (to be clear, I [also answered it once](https://stackoverflow.com/a/71640165/7884305)). – Chayim Friedman Jun 05 '22 at 20:38
  • @ChayimFriedman you can always bring it up in the [Rust chatroom](https://chat.stackoverflow.com/rooms/62927/rust), there's many people that will help curate. :) – kmdreko Jun 05 '22 at 20:47
  • As I am currently a Rust beginner who is still quite unfamiliar with the language and the lingo surrounding it I don't think I'd have been able to find the question that mine is currently marked a duplicate of. Additionally even if I had found it, I think mine is easier to grasp at first glance (the linked one deals with traits, closures, etc..., as a beginner the return type of the function in the linked question makes no sense to me *yet* (`-> impl Iterator<...>`), i.e. I don't see any mention of lifetimes `&'...` which my question is centered around), so ultimately I opened this one. – bool3max Jun 05 '22 at 21:21
  • 1
    @bool3max I definitely didn't forward my rant to you. It is completely understandable that OPs, especially beginners, do not find this duplicate (it is pretty hard to do if you don't know the name, "static promotion"). I was complaining about the fact the _the answerers_ don't find it. – Chayim Friedman Jun 05 '22 at 21:25

1 Answers1

2

The line return &MyType::Two is an example of "static promotion", as described (among others) in RFC 1414.

The compiler can see that the value behind the reference is a constant, and "instead of translating the value into a stack slot, translate it into a static memory location and give the resulting reference a 'static lifetime." That is, in another example,

let a = &Some(32);

... the compiler will not create a literal value in the scope of the current function and take a reference to that, but rather bake the value into the final executable and promote the reference to a &'static. So it is implicitly

let a: &'static Option<u32> = &Some(32);

This is why in your second example the reference you return is a &'static MyType, which lives at least as long as any &'a MyType the generic 'a could turn out to be.

See the RFC for examples when "static promotion" can apply and when it can't, as it is not always possible.

The reason this does not work in your first example is that the code definitly creates the value in the function's local scope and takes a reference to that, which can't be promoted to a 'static.

user2722968
  • 13,636
  • 2
  • 46
  • 67