Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does taking a static reference to a const return a reference to a temporary variable?

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
    }
}
like image 377
Zachary Burns Avatar asked Dec 06 '22 10:12

Zachary Burns


2 Answers

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:

  • What is the difference between immutable and const variables in Rust?
  • What is the difference between a const variable and a static variable and which should I choose?
like image 93
Sven Marnach Avatar answered Jan 23 '23 05:01

Sven Marnach


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.

like image 22
Peter Hall Avatar answered Jan 23 '23 06:01

Peter Hall