Search code examples
rustreferenceglobal-variables

How to return a reference to a global variable OnceLock?


I'm trying to implement a derive-macro which would implement a trait for handling global variables. Say, there're some enum types and I would like to have a separate OnceLock for each enum. In that case, there's a need for a handle to get it in a general way. The problem lies in the returned lifetimes.

use std::sync::OnceLock;

const v: OnceLock<Vec<u32>> = OnceLock::new();

trait GetGlobal: Sized {
    fn get_global() -> &'static OnceLock<Self>;
}

impl GetGlobal for Vec<u32> {
    fn get_global() -> &'static OnceLock<Self> {
        &v
    }
}

fn main() {
}

The code above generates the following error:

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

How should I go about remedying it? Not to mention, that it would be better off returning a reference to the underlying data, via OnceLock::get(). Still, it spawns the same lifetime error.


Solution

  • Having v as a constant won't work. Roughly speaking, constants evaluate to expressions that are copied to the locations they are used in, so your &v becomes &OnceLock::new(), hence creating a temporary value. static items on the other hand are a concrete location in memory. Make your OnceLock a static variable. From the docs of static (emphesized by me):

    On the surface, static items seem very similar to consts: both contain a value, both require type annotations and both can only be initialized with constant functions and values. However, statics are notably different in that they represent a location in memory. That means that you can have references to static items and potentially even modify them, making them essentially global variables.

    Here's your code with static replacing const and it compiles on the Playground:

    use std::sync::OnceLock;
    
    static V: OnceLock<Vec<u32>> = OnceLock::new();
    
    trait GetGlobal: Sized {
        fn get_global() -> &'static OnceLock<Self>;
    }
    
    impl GetGlobal for Vec<u32> {
        fn get_global() -> &'static OnceLock<Self> {
            &V
        }
    }
    
    fn main() {
    }