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?