[newbie] static mut struct and instanciation

Hello,

I'd like to use a global variable in a multi-threading application.
I naively wrote this code :

use std::sync::{Arc, Mutex};

struct UserSession {
    hash: u128,
    user: String,
}

struct Global {
    session_list: Arc<Mutex<Vec<UserSession>>>,
}
impl Global {
    fn new() -> Self {
        Self {
            session_list: Arc::new(Mutex::new(Vec::new())),
        }
    }
}
static mut GLOBAL: Global = Global::new();

fn main() {
    /* ...CODE... */
}

This doesn't compile, recommending to use LazyBlock :

error[E0015]: cannot call non-const fn `Global::new` in statics
  --> src/main.rs:18:29
   |
18 | static mut GLOBAL: Global = Global::new();
   |                             ^^^^^^^^^^^^^
   |
   = note: calls in statics are limited to constant functions, tuple structs and tuple variants
   = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)`

But why this need of LazyLock ? Isn't there a way to initialize static variables at startup ?

You can just make your new function const.

const fn new() -> Self {

Aside from that, this shouldn't be static mut, just static, and you don't need Arc (you need to remove Arc to make new const anyway).

3 Likes

I do this:

use std::sync::{LazyLock, Mutex};

struct UserSession {
    hash: u128,
    user: String,
}

struct Global {
    session_list: Mutex<Vec<UserSession>>,
}
impl Global {
    const fn new() -> Self {
        Self {
            session_list: Mutex::new(Vec::new()),
        }
    }
}
static GLOBAL: LazyLock<Global> = Global::new();

fn main() {
    /* ...CODE... */
}

but it throws this compilation error :

18 | static GLOBAL: LazyLock<Global> = Global::new();
   |                                   ^^^^^^^^^^^^^ expected `LazyLock<Global>`, found `Global`
   |
   = note: expected struct `LazyLock<Global>`
              found struct `Global`

Have I missed something ?

You don't need LazyLock.

2 Likes

LazyLock is a way to effectively safely initialize static variables at startup (first use), with synchronization.

There is no guaranteed single-threaded pre-main-esque mechanism built into to the language (which would allow safe static initialization without synchronization).


But you don't need any of that since you can initialize at compile time (const fn new).

static GLOBAL: Global = Global::new();
3 Likes

Haaa sorry ! Thanks it compiles well ! And @quinedot for the explainations !

1 Like

If you ever need LazyLock in future, note that your initialization should be:

static GLOBAL: LazyLock<Global> = LazyLock::new(|| Global::new());

The closure is the code that's run the first time someone accesses GLOBAL, or if someone explicitly calls GLOBAL.force() to make sure it's been initialized (which you can do from - for example - main if you need to make sure it's not going to run the initialization closure at an inconvenient time).

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.