From c37c6fe877f40439367b4f4c6843878ecb898788 Mon Sep 17 00:00:00 2001 From: Eren Atas Date: Mon, 23 Feb 2026 21:29:06 +0100 Subject: [PATCH 1/3] refactor!: replace lazy_static with std::sync::OnceLock This change enables Webassembly compatibility by replacing `lazy_static` with `syd::sync:OnceLock` and tokio feature changes. It should be considered breaking due MSRV change. This change doesn't have any performance impact on the library itself. tokio's `sync` feature provides `RwLock` which works identically regardless of runtime. Users of this library can enable tokio's multi-threaded runtime if needed. Changes in summary: - Replace lazy_static crate with std::sync::OnceLock for singleton - Bump MSRV from 1.77.0 to 1.80.1 - Remove lazy_static dependency - Split tokio features: sync for library, rt-multi-thread/macros for tests - Update doc comment to reference public API methods Signed-off-by: Eren Atas --- Cargo.toml | 6 +++--- src/api/api.rs | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6212d57..ab43fef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "open-feature" version = "0.2.7" edition = "2021" -rust-version = "1.77.0" # MSRV +rust-version = "1.80.1" # MSRV description = "The official OpenFeature Rust SDK." documentation = "https://docs.rs/open-feature" readme = "README.md" @@ -17,11 +17,10 @@ maintenance = { status = "actively-developed" } [dependencies] async-trait = "0.1.80" -lazy_static = "1.5" mockall = { version = "0.14.0", optional = true } serde_json = { version = "1.0.116", optional = true } time = "0.3.36" -tokio = { version = "1.40", features = ["full"] } +tokio = { version = "1.40", features = ["sync"] } typed-builder = "0.22.0" log = { package = "log", version = "0.4", optional = true } @@ -30,6 +29,7 @@ log = { package = "log", version = "0.4", optional = true } env_logger = "0.11.5" structured-logger = "1.0.3" spec = { path = "spec" } +tokio = { version = "1.40", features = ["sync", "rt-multi-thread", "macros"] } [features] default = ["test-util", "dep:log"] diff --git a/src/api/api.rs b/src/api/api.rs index 3344be7..a81879b 100644 --- a/src/api/api.rs +++ b/src/api/api.rs @@ -1,4 +1,5 @@ -use lazy_static::lazy_static; +use std::sync::OnceLock; + use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::{ @@ -11,14 +12,16 @@ use super::{ provider_registry::ProviderRegistry, }; -lazy_static! { - /// The singleton instance of [`OpenFeature`] struct. - /// The client should always use this instance to access OpenFeature APIs. - static ref SINGLETON: RwLock = RwLock::new(OpenFeature::default()); +/// The singleton instance of [`OpenFeature`] struct. +/// The client should always use this instance to access OpenFeature APIs. +static SINGLETON: OnceLock> = OnceLock::new(); + +fn singleton_lock() -> &'static RwLock { + SINGLETON.get_or_init(|| RwLock::new(OpenFeature::default())) } /// THE struct of the OpenFeature API. -/// Access it via the [`SINGLETON`] instance. +/// Access it via [`OpenFeature::singleton()`] or [`OpenFeature::singleton_mut()`]. #[derive(Default)] pub struct OpenFeature { evaluation_context: GlobalEvaluationContext, @@ -30,12 +33,12 @@ pub struct OpenFeature { impl OpenFeature { /// Get the singleton of [`OpenFeature`]. pub async fn singleton() -> RwLockReadGuard<'static, Self> { - SINGLETON.read().await + singleton_lock().read().await } /// Get a mutable singleton of [`OpenFeature`]. pub async fn singleton_mut() -> RwLockWriteGuard<'static, Self> { - SINGLETON.write().await + singleton_lock().write().await } /// Set the global evaluation context. From c0bc814b3badc7e55bbb781f7b551e815ce54baf Mon Sep 17 00:00:00 2001 From: Eren Atas Date: Mon, 23 Feb 2026 21:38:15 +0100 Subject: [PATCH 2/3] Update src/api/api.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Eren Atas --- src/api/api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/api.rs b/src/api/api.rs index a81879b..ca8d52e 100644 --- a/src/api/api.rs +++ b/src/api/api.rs @@ -16,7 +16,7 @@ use super::{ /// The client should always use this instance to access OpenFeature APIs. static SINGLETON: OnceLock> = OnceLock::new(); -fn singleton_lock() -> &'static RwLock { +fn get_singleton() -> &'static RwLock { SINGLETON.get_or_init(|| RwLock::new(OpenFeature::default())) } From 21f14bf65f32c834e437b85807cf10b847c0a51f Mon Sep 17 00:00:00 2001 From: Eren Atas Date: Mon, 23 Feb 2026 21:38:44 +0100 Subject: [PATCH 3/3] fix after gemini change Signed-off-by: Eren Atas --- src/api/api.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/api.rs b/src/api/api.rs index ca8d52e..4207957 100644 --- a/src/api/api.rs +++ b/src/api/api.rs @@ -33,12 +33,12 @@ pub struct OpenFeature { impl OpenFeature { /// Get the singleton of [`OpenFeature`]. pub async fn singleton() -> RwLockReadGuard<'static, Self> { - singleton_lock().read().await + get_singleton().read().await } /// Get a mutable singleton of [`OpenFeature`]. pub async fn singleton_mut() -> RwLockWriteGuard<'static, Self> { - singleton_lock().write().await + get_singleton().write().await } /// Set the global evaluation context.