Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Static lifetime deduced for constant, but not for return values of const fn #90370

Closed
hniksic opened this issue Oct 28, 2021 · 4 comments
Closed

Comments

@hniksic
Copy link
Contributor

hniksic commented Oct 28, 2021

In rust borrowed constants have static lifetimes, even if the constants are simple literals in the middle of a function. For example, this compiles:

fn assert_static(_r: &'static u32) {}
fn main() {
    assert_static(&1); // compiles
}

This works even in more involved situations, such as when the "literal" is a macro-generated array of structs whose fields are literals. (That's my actual use case, but I'll refer to the simple case of just having a u32 for simplicity.)

The problem is that it no longer works if the value is returned by a const fn:

const fn inc(n: u32) -> u32 {
    n + 1
}

fn assert_static(_r: &'static u32) {}

fn main() {
    assert_static(&1);
    assert_static(&inc(1)) // doesn't compile
}

Playground

error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:9:20
   |
9  |     assert_static(&inc(1)) // doesn't compile
   |     ---------------^^^^^^-
   |     |              |
   |     |              creates a temporary which is freed while still in use
   |     argument requires that borrow lasts for `'static`
10 | }

I would expect (or rather wish) for the latter code to compile since the former did. Note that, since const and static are static, it is possible to work around it by creating an explicit const or static inbetween. For example, all of these compile:

fn main() {
    assert_static(&1);
    {
        const TMP: u32 = inc(1);
        assert_static(&TMP);
    }
    {
        static TMP: u32 = inc(1);
        assert_static(&TMP);
    }
    assert_static({
        const TMP: u32 = inc(1);
        &TMP
    });
    assert_static({
        static TMP: u32 = inc(1);
        &TMP
    });
}

Playground

While a workaround is available, it is not always obvious. Also it would be much nicer not to have to introduce all the temporary constants/statics, especially in code that calls several different const fns.

@SkiFire13
Copy link
Contributor

This seems to be intended behaviour, see rust-lang/const-eval#19, RFC 3027 and the const-eval documentation on promotion

@PatchMixolydic
Copy link
Contributor

PatchMixolydic commented Oct 31, 2021

The inline_const feature gives a less noisy way to make this code compile (playground):

#![allow(incomplete_features)]
#![feature(inline_const)]

const fn inc(n: u32) -> u32 {
    n + 1
}

fn assert_static(_r: &'static u32) {}

fn main() {
    assert_static(&1);
    assert_static(&const { inc(1) });
}

@RalfJung
Copy link
Member

RalfJung commented Nov 3, 2021

Indeed, this is expected behavior -- promoting const fn implicitly can lead to compile errors in code that really ought to compile, besides leading to nightmare consequences within rustc itself. The key difference between &1 and &const_fn() is that the latter might panic or fail to evaluate for other reasons, so we cannot easily treat them the same.

So I will close this issue; the intended convenient way to write such code in the future is via inline consts, tracked at #76001.

@RalfJung RalfJung closed this as completed Nov 3, 2021
@RalfJung
Copy link
Member

RalfJung commented Nov 3, 2021

Oh, also, a shameless plug: I have a little crate that partially implements inline consts as a macro -- https://crates.io/crates/inline-const.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants