Why can I return a reference to a local literal but not a variable?
Asked Answered
T

1

50

Why does this code compile?

fn get_iter() -> impl Iterator<Item = i32> {
    [1, 2, 3].iter().map(|&i| i)
}

fn main() {
    let _it = get_iter();
}

[1, 2, 3] is a local variable and iter() borrows it. This code should not compile because the returned value holds a reference to a local variable.

Theocritus answered 15/5, 2018 at 7:59 Comment(0)
S
67

In your example, [1, 2, 3] is not treated as local variable, but as static one!

Let's take a look at this code:

fn foo() -> &'static [i32] {
    &[1, 2, 3]
}

This works!

Some time ago, RFC 1414: Rvalue Static Promotion was merged: "Promote constexpr rvalues to values in static memory instead of stack slots". This means that basically all literals you write can live forever. Thus, things like let _: &'static i32 = &42; also work!

If we avoid using a literal array, we can see the expected error:

fn bar() -> impl Iterator<Item = i32> {
    vec![1, 2, 3].iter().map(|&i| i)
}

Here we get the "v does not live long enough" error.

This isn't limited to integers or arrays; it applies broadly to any literal that is composed solely of literals:

fn promote_integer() -> &'static i32 {
    &42
}
fn promote_float() -> &'static f64 {
    &42.42
}
fn promote_str() -> &'static str {
    "Hello World!"
}
struct Foo(char);

fn promote_struct() -> &'static Foo {
    &Foo('x')
}

Beyond literals, this also works for a tiny number of functions in the standard library, but these were likely a mistake. Deciding on if the result of arbitrary const functions can be automatically promoted to static is still an open topic.

Selfcontent answered 15/5, 2018 at 8:3 Comment(8)
It seems that the const variables are obsolete :pTheocritus
@Theocritus that's an interesting idea. Have you found discuss on that that you could link?Eam
@Eam I saw somewhere that (maybe) one day, the const functions could be detected by the compiler without any hint. However, nothing was said about const variables, and I do not find any link right now.Theocritus
@Theocritus They are not obsolete. This only works for literals. Sometimes you need a const that must be computed.Royo
@PeterHall Hum, you are right; but in theory, the compiler could know without hint what is const and what isn't.Theocritus
@Theocritus There are some downsides to storing data in static memory, especially if the data is large, and not required for the full lifespan of the program. There are definitely times when you should have to opt in to the behaviour.Royo
Does the use of &42 force the compiler to store 42 in the static memory instead of inlining it?Horse
@Horse Yes, that's what the language model says. However, the optimizer can still inline the value later anyway.Selfcontent

© 2022 - 2024 — McMap. All rights reserved.