Hi y'all. I am running into a problem with the LazyLock I cannot currently wrap my head around.
I was trying to create a static regex by using a variable that was passed into the function. Mandatory playground link Code below:
use regex::Regex;
use std::sync::LazyLock;
struct Config {
tenant_id: String
}
fn regex(config: &Config) -> &'static Regex {
static RE: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(&format!("^{}-[^-]+-[^-]+(-[^-]+)?$", regex::escape(&config.tenant_id)))
.expect("Failed to create the user validation regex")
});
&RE
}
The example in the documentation works and does compile correctly. However, when trying to compile the code, it gave me the error: error[E0435]: attempt to use a non-constant value in a constant.
I tried creating a string first, similar to the example and that also didn't work. I'm not sure why that happens, as far as I can reason, my usage of LazyLock here should be completely safe. As long as the result does not hold onto a reference of config it shouldn't be a problem right? What am I missing here?
A static is an item (that is, something that can go outside a function), so it can't use anything outside the function scope. If this were allowed, it might cause problems with LazyLock, because LazyLock runs its closure if it hasn't been initialised when its value is requested, and that could happen before your function is called.
For your case, something like OnceLock, where the closure can be passed in at runtime, may work better than LazyLock. You could also use a pair of statics, one with a OnceLock for config and one with a LazyLock for the regex.
Personally, I like your idea of moving the compiled regex and the regex() function to the Config struct. It's clean, it makes the association clear, and it allows the OnceLock to be deallocated if and when the time comes. Of course, I have no idea how the code is being used or which code structure is a better fit for the application. In the interest of trying to completely answer the question, I'll offer an alternative implementation that hews more closely to the original:
use regex::Regex;
use std::sync::OnceLock;
struct Config {
tenant_id: String,
}
fn regex(config: &Config) -> &'static Regex {
static RE: OnceLock<Regex> = OnceLock::new();
RE.get_or_init(|| {
Regex::new(&format!(
"^{}-[^-]+-[^-]+(-[^-]+)?$",
regex::escape(&config.tenant_id)
))
.expect("Failed to create the user validation regex")
})
}