[newbie] cannot call non-const fn `Arc::<Mutex<Config>>::new` in constant functions

Hello,

A newbie question.
I've a static variable calls "Global", containing some Struct. To initialize one I have to call a function. The compiler send me an error :
cannot call non-const fn Arc::<Mutex>::new in constant functions

I've seen there's LazyLock but I don't understand how to use it.

The illustration :

The purpose of Arc is to make a value shared. However, using a global variable also makes a value shared. You do not need multiple mechanisms for sharing, so just get rid of the Arc.

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

struct Global {
    config: Mutex<Config>,
}
impl Global {
    const fn new() -> Self {
        Self {
            config: Mutex::new(  read_config()  ),
        }
    }
}

Also, I removed the mut in static mut. You should pretty much never use static mut since it requires unsafe.

5 Likes

Thanks ! I've reviewed my code, and adding the use of LazyLock.
This code runs well :

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

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

struct Global {
    config: Mutex<LazyLock<Config>>,
}

impl Global {
    const fn new() -> Self {
        Self {
            config: Mutex::new(LazyLock::new(|| read_config())),
        }
    }
}

#[derive(Deserialize)]
pub struct Config {
    port: u16,
}

pub fn read_config() -> Config {
    let content = std::fs::read_to_string("config.toml").unwrap();
    toml::from_str(&content).unwrap()
}

fn main() {
    let locked = GLOBAL.config.lock().unwrap();
    println!("port={}", locked.port);
}

You don't need LazyLock when using Mutex. You can just use Option.

3 Likes

Sorry, I don't find how to compile without using LazyLock. Where have I to use Some ?
Have you an example of a static variable initiate by a non const function ?

The point is that you should either use Mutex<Option<Config>> or LazyLock<Config>, depending on whether you'll plan to mutate the Config or not.

  • if you don't need to mutate the Config when you can just use LazyLock<Config> instead of your current Mutex<LazyLock<Config>>; Rust Playground

  • if you do need to mutate the Config then you can use Mutex<Option<Config>>, initializing it with Mutex::new(None) and then populating the Option with Some(non_const_function()) when you first access it (i.e. when it's None). Rust Playground

4 Likes

A great great thank ! :partying_face: