Rules for returning references to constant expressions

Hello! I have recently come across a piece of code which I initially thought was incorrect and wouldn't compile but it did compile just fine.

pub fn create_bar() -> Bar {
    let foo = &Foo { _val: 123 };
    Bar::new(foo)
}

Playground

I read this as "create an anonymous local variable of type Foo, get a reference to it, pass it to Bar, and return from the function". That would obviously not be okay. And indeed when I rewrote it a little bit I got an error I would have expected from the first example.

// ERROR: borrowed value does not live long enough
pub fn create_bar() -> Bar {
    let foo = Foo { _val: 123 };
    Bar::new(&foo)
}

Playground

I searched the internet and I think I found the answer from @kornel. That is, Rust is smart enough to realize that what I'm creating is essentially a constant and turns it into a static and returns a reference to it. If I modify the example to take a String instead of u32, it no longer compiles. Presumably because at that point it's no longer considered a constant expression.

Finally, to the questions. Is my interpretation of what's happening correct? How can I tell that a struct can be treated this way? Is there a documentation for this?

Thank you.

1 Like

Looks like existing documentation ist pretty bad. There is this section in the reference Destructors - The Rust Reference and this RFC 1414-rvalue_static_promotion - The Rust RFC Book but both seem out of sync with the actual implementation which is more conservative and only allows syntactically simple stuff like e.g. constructors, but no function calls (not const fn either). The rationale probably being that if such function calls were to not terminate, i. e. panic or loop forever (or just take a long time to terminate), then you would expect this as a run-time error, not a compile-time error. [1]


  1. Unconditionally panicking operations are sometimes turned into a warning, but hard errors are breaking changes here; while a panic at compile time could be turned into a run-time panic, instances of const evaluation not terminating fast enough could not be fixed and would need to become a compile time error, since the 'static lifetime would promise it couldn't happen at run time anymore even as a fallback. ↩︎

3 Likes

Implicit rvalue constant promotion was restricted by RFC#3027 to expressions which are guaranteed to never fail to evaluate.

The current guidelines for constant promotion are documented, but the intent is to slowly claw back places where rustc has done overeager rvalue promotion and use const blocks to explicitly get constant promotion instead. This becomes even more important if/when we make constant evaluation observable, as once that's the case promotion can change the behavior of code.

IIRC, the intent is that rvalue promotion should eventually get restricted back to purely just literal syntax (this includes struct literals), non user-overloaded operators, and uses of const items.

The problem with this simple approach is that it would also make &0 and &CONST_ITEM not able to be &'static. Some amount of constant promotion is clearly desirable; the question thus is just where do you place the cutoff.

4 Likes

Perfect, thanks for finding and linking these additional sources, especially the RFC. Arguably, the section in the reference should be more precise nonetheless.

Also… why don’t we add links from older RFCs to newer RFCs that modify/update the proposed behavior? IMO the RFC 1411 should definitely get a link to the RFC 3027, preferably well visible and at the top of the page.

1 Like

We certainly do sometimes, like 2203-const-repeat-expr - The Rust RFC Book -- feel free to send a PR for more like that and I'll approve it.

3 Likes

Thank you for this.

Are there any particular tags or other guidelines for such PRs? I think over 10% of accepted RFCs have been superceded, withdrawn, only partially implemented, or otherwise diverged (often without a corresponding documentation update, and usually without adding a note to the RFC text).

The not-rfc tag, mostly.

RFCs aren't intended as up-to-date documentation of the language (that would be the reference), so the only edits to previous RFCs I'd say are semi conventional are to add a note when an RFC has been superceded by a later RFC or unaccepted, but not just for implementation changes between RFC and stable.

From https://github.com/rust-lang/rfcs/#the-rfc-life-cycle,

In general, once accepted, RFCs should not be substantially changed.

Arguably the most important thing about most RFCs is the justifications, which are hopefully not substantially changing -- if they are, that would be a good reason for a new RFC.

I wrote up a whole thing on the limits of the reference and why heads-up about changes and significant changes are very valuable, but deleted it because it came off too much like a rant. Long story short, despite generally quite good library documentation I find Rust's language documentation to be extremely lacking [1], find that the RFCs are significant in filling part of the gap, and feel that enhancing and maintaining their usefulness would be valuable.

I know all that, and I'm not looking to make significant changes either, just an informative box at the top for those who care enough about the language to try and understand it in more depth.

Incidentally, I've read basically that paragraph before as a reason to not bother, which is very demotivating. [2] Which it why it made me very happy to see

instead of the usual discouragement and expectation of no improvement.


Sorry if this still came off as a rant, but it's an extremely frustrating situation. I really hope we get a spec and it's accuracy has a better hit-rate than the current language documentation. (But even if we do it will take years, and I suspect the RFCs will remain a valuable source for more in-depth learning and understanding of the language.)


  1. especially below the surface ↩︎

  2. Especially given how efforts to improve other documentation has often also gone nowhere, but that's probably a workload issue as much as anything. ↩︎

6 Likes

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.