4

In Rust I have the following code:

pub trait Test: Sized {
    const CONST: Self;
    fn static_ref() -> &'static Self {
        &Self::CONST
    }
}

My expectation is that since const is 'static, then I should be able to take a reference to it that is also 'static. However, the compiler gives the following error:

error[E0515]: cannot return reference to temporary value
   --> file.rs:9:9
    |
  9 |         &Self::CONST
    |         ^-----------
    |         ||
    |         |temporary value created here
    |         returns a reference to data owned by the current function

How is a temporary variable being introduced here?

Additionally, it seems that there are some cases where taking a reference to a constant does work. Here is a short concrete example with a slightly different implementation of Test

pub trait Test: Sized {
    fn static_ref() -> &'static Self;
}

struct X;

impl Test for X {
    fn static_ref() -> &'static Self {
        &X
    }
}
hellow
  • 12,430
  • 7
  • 56
  • 79
Zachary Burns
  • 453
  • 2
  • 9
  • It seems that you are trying to extend the lifetime of CONST over the lifetime of the function. When you exit function, the reference to CONST isn't valid. – miimote Mar 29 '19 at 18:33

3 Answers3

12

A constant in Rust is a compile-time constant, not an actual variable with a memory location. The Rust compiler can substitute the actual value of the constant whereever it is used. If you take the address of such a value, you get the address of a temporary.

Rust also has the concept of a static variable. These variables actually have memory locations that are consistent for the whole program duration, and taking a reference to a static variable indeed results in a reference with 'static lifetime.

See also:

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • This seems inconsistent with some of the documenation. By way of example, I can do this...impl Test for X { fn static_ref() -> &'static Self { &X{} } } In this example, the value X{} is a constant, and yet I take a reference to it. – Zachary Burns Mar 29 '19 at 18:38
  • I will edit the question to add this additional wrinkle. – Zachary Burns Mar 29 '19 at 18:43
  • @ZacharyBurns I can't follow the example you gave in the comment. I suggest asking a new question if this one wasn't enough, or at least giving a playground link for the example, so we can follow it. It would also be helpful to give a pointer to the documentation you think is wrong. – Sven Marnach Mar 29 '19 at 18:44
  • I have edited my question to clarify how it is sometimes possible to take a reference to a constant. I am looking for the documentation and will update the comment shortly. – Zachary Burns Mar 29 '19 at 18:50
  • what's a temporary? – joel Aug 25 '19 at 16:55
  • @joel A temporary is an anonymous intermediate result in an expression which only live for the duration of the current statement. – Sven Marnach Aug 25 '19 at 18:04
4

When you define a trait, the definition must make sense for all possible implementations.

The problem may not be immediately clear without an example of where it fails. So suppose you had a type like this:

struct MyStruct;
impl MyStruct {
    const fn new() -> Self {
        MyStruct
    }
}

And you attempted to implement the trait like this:

impl Test for MyStruct {
    const CONST: Self = MyStruct::new();
}

This won't work because the implementation of static_ref will now look like this:

fn static_ref() -> &'static Self {
    // &Self::CONST
    &MyStruct::new()
}

It's creating a value inside the function and trying to return it. This value is not static, so the 'static lifetime is invalid.


However, with a little re-jigging, you can make something work:

pub trait Test: Sized + 'static {
    // This is now a reference instead of a value:
    const CONST: &'static Self;
    fn static_ref() -> &'static Self {
        Self::CONST
    }
}

struct MyStruct;
impl MyStruct {
    const fn new() -> Self {
        MyStruct
    }
}

impl Test for MyStruct {
    const CONST: &'static Self = &MyStruct::new();
}

This works because CONST is already a 'static reference, so the function can just return it. All possible implementations would have to be able to obtain a 'static reference to Self to implement the trait, so there is no longer an issue with referencing some arbitrary local value.

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
1

The mechanism at work here is static promotion. See RFC 1414

Here's a quote:

Inside a function body's block:

  • If a shared reference to a constexpr rvalue is taken. (&)
  • And the constexpr does not contain a UnsafeCell { ... } constructor.
  • And the constexpr does not contain a const fn call returning a type containing a UnsafeCell.
  • Then instead of translating the value into a stack slot, translate it into a static memory location and give the resulting reference a 'static lifetime.
Bockwurst
  • 11
  • 2