matklad / once_cell

Rust library for single assignment cells and lazy statics without macros

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Is it possible to use sync::Lazy together with constants/static?

REASY opened this issue · comments

Hello,

I'm trying to get the following code to compile, but didn't succeed.

/*
[dependencies]
once_cell = "1.18.0"

*/
use once_cell::sync::Lazy;

#[derive(Debug, Clone)]
pub struct Settings {
    pub environment: String,
    pub path: String,
    pub zoom: u32
}


impl Settings {
    pub fn new() -> Settings {
        Settings { environment: "dev".to_string(), path: "foo".to_string(), zoom: 1u32 }
    }
}

pub static SETTINGS: Lazy<Settings> = Lazy::new(|| {
    Settings::new()
});


static ZOOM: u32 = SETTINGS.zoom;

const CONST_ZOOM: u32 = SETTINGS.zoom;

fn main() {
    println!("{:?}", SETTINGS);
}

The errors:

error[E0015]: cannot perform deref coercion on `once_cell::sync::Lazy<Settings>` in statics
    --> src/main.rs:28:20
     |
28   | static ZOOM: u32 = SETTINGS.zoom;
     |                    ^^^^^^^^^^^^^
     |
     = note: attempting to deref into `Settings`
note: deref defined here


error[E0013]: constants cannot refer to statics
  --> src/main.rs:30:25
   |
30 | const CONST_ZOOM: u32 = SETTINGS.zoom;
   |                         ^^^^^^^^
   |
   = help: consider extracting the value of the `static` to a `const`, and referring to that

error[E0015]: cannot perform deref coercion on `once_cell::sync::Lazy<Settings>` in constants
    --> src/main.rs:30:25
     |
30   | const CONST_ZOOM: u32 = SETTINGS.zoom;
     |                         ^^^^^^^^^^^^^

Playground at rustexplorer

Thank you.

Lazy is initialized on acess, so ZOOM would also be Lazy<u32> and CONST_ZOOM is just not possible.

There is actually an alternative to using Lazy here, if you are willing to change Settings to:

use std::borrow::Cow;

#[derive(Debug, Clone)]
pub struct Settings {
    pub environment: Cow<'_, str>,
    pub path: Cow<'_, str>,
    pub zoom: u32
}

impl Settings {
    pub const fn new() -> Settings {
        Settings { environment: Cow::Borrowed("dev"), path: Cow::Borrowed("foo"), zoom: 1u32 }
    }
}

pub const SETTINGS: Settings = Settings::new();

static ZOOM: u32 = SETTINGS.zoom;

const CONST_ZOOM: u32 = SETTINGS.zoom;

Or you can replace String with CompactString, which has a const constructor CompactString::new_inline for strings that are no longer than 24 bytes on 64-bit platform and strings that are no longer than 12 bytes on 32-bit platform.

Hi, @NobodyXu, thank you the answer!

I cannot go with const SETTINGS because of my use-case, I'm loading settings from config file using config crate, so cannot be const, right?

This is the full example of what I would want to achieve, compiler errors with:

error[E0015]: cannot perform deref coercion on `once_cell::sync::Lazy<Settings<'_>>` in statics
    --> src/main.rs:85:56
error[E0015]: cannot perform deref coercion on `Cow<'_, str>` in statics
  --> src/main.rs:85:55
error[E0015]: cannot call non-const fn `get_gdal_vsi_path` in statics
  --> src/main.rs:85:37

Compiler is quite straightforward in the last message, "cannot call non-const fn get_gdal_vsi_path in statics. So there is no way to achieve what I want, having static variable initialized only once instead of creating it in the method always?

Thanks.

I cannot go with const SETTINGS because of my use-case, I'm loading settings from config file using config crate, so cannot be const, right?

In that case, yes, you would have to use OnceCell or arc-swap.

In your example:

// Does not compile. I want this to be initialized only once instead of local variable in main
static GDAL_VSI_BASE_PATH: Lazy<String> = Lazy::new(|| get_gdal_vsi_path(&SETTINGS.tile_store.base_path));

Also, OnceCell::get_or_try_init allow you to call fallible function and return error instead of having to panic.

Thank you, @NobodyXu !

I assume the runtime overhead of the type being lazy is negligible, the case of OnceCell lazy there is nothing heavy in force method?

Thank you, @NobodyXu !

You are welcome

I assume the runtime overhead of the type being lazy is negligible, the case of OnceCell lazy there is nothing heavy in force method?

No, it's just the same as OnceCell.

It's generally quite cheap.