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
}
}
const
and static
are different things, but are often confused with each other.
static
bindings hold data that lives in a permanent memory location for the duration of the program. That's why you can return references to them from functions; they live forever so you can give them the 'static
lifetime.
const
bindings have values that don't change and can be reasoned about at compile-time just as well as at run-time. The compiler will inline the expression used to define the const
everywhere that it is used. It's likely that two usages of the same constant will store it in different memory addresses and may even reapply the computation used to construct it.
Consider a slightly more complicated example:
struct MyStruct;
impl MyStruct {
const fn new() -> Self {
MyStruct
}
}
impl Test for MyStruct {
const CONST: Self = MyStruct::new();
}
This won't work because, since constants are inlined, the implementation of static_ref
will now look like this:
fn static_ref() -> &'static Self {
&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 a temporary value.