aboutsummaryrefslogtreecommitdiff
path: root/mingling_core/src/asset/lazy_resource.rs
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-06-07 22:38:53 +0800
committer魏曹先生 <1992414357@qq.com>2026-06-07 22:38:53 +0800
commit991218088e4253468ea3647814ab2ff7f5294845 (patch)
tree300bcc77c0a053c75f8133ddf93bc1d7fa4a97ac /mingling_core/src/asset/lazy_resource.rs
parent9725bbc5a41e00a2b952cdfe4a04d472765c9e41 (diff)
Refactor LazyRes to use enum and drop factory after initHEADmain
Diffstat (limited to 'mingling_core/src/asset/lazy_resource.rs')
-rw-r--r--mingling_core/src/asset/lazy_resource.rs185
1 files changed, 113 insertions, 72 deletions
diff --git a/mingling_core/src/asset/lazy_resource.rs b/mingling_core/src/asset/lazy_resource.rs
index 41f8a34..356e348 100644
--- a/mingling_core/src/asset/lazy_resource.rs
+++ b/mingling_core/src/asset/lazy_resource.rs
@@ -1,98 +1,125 @@
use crate::{ProgramCollect, ResourceMarker, this};
-/// A lazily initialized resource that only creates its value on first access via a given initializer function.
-///
-/// `LazyRes<T>` wraps an `Option<T>` and an initializer function, supporting thread-safe lazy loading.
-/// Initialization is triggered by calls to `get_ref()`, `get_mut()`, or `get_clone()`.
-pub struct LazyRes<T: Default + Send + Sync + Clone> {
- /// The initializer function, called on first access to produce the inner value.
- init_fn: Box<dyn FnMut() -> T + Send + Sync>,
- /// Stores the initialized value; `None` means not yet initialized.
- inner: Option<T>,
+enum LazyInner<T> {
+ /// Not yet initialized — holds the factory function.
+ /// After init, the factory is **dropped**, no wasted memory.
+ Uninit(Box<dyn FnMut() -> T + Send + Sync>),
+ /// Initialized and ready to go.
+ Init(T),
}
-impl<T: Default + Send + Sync + Clone> Default for LazyRes<T> {
- /// Creates an uninitialized `LazyRes<T>` whose initializer returns `T::default()`.
- fn default() -> Self {
- Self {
- inner: None,
- init_fn: Box::new(|| T::default()),
- }
- }
+/// A lazily initialized resource that only creates its value on first access.
+///
+/// Unlike `Option<T>` + persistent `Box<dyn FnMut>`, the factory function is
+/// **consumed and dropped** after initialization. No leftover allocation.
+///
+/// Initialization is triggered by `get_ref()`, `get_mut()`, or `get_clone()`.
+pub struct LazyRes<T: Send + Sync + 'static> {
+ inner: LazyInner<T>,
}
-impl<T: Default + Send + Sync + Clone + 'static> LazyRes<T> {
- /// Creates a new lazily initialized resource with a custom initializer function.
- ///
- /// # Parameters
- /// - `f`: The initializer function called on first access, returning a value of type `T`.
+impl<T: Send + Sync + 'static> LazyRes<T> {
+ /// Creates a new lazily initialized resource with a custom initializer.
///
- /// # Returns
- /// Returns an uninitialized `LazyRes<T>`.
+ /// The factory `f` is called on first access, then dropped.
#[must_use]
pub fn new(f: impl FnMut() -> T + Send + Sync + 'static) -> Self {
Self {
- inner: None,
- init_fn: Box::new(f),
+ inner: LazyInner::Uninit(Box::new(f)),
}
}
- /// Returns an immutable reference to the inner value, calling the initializer if necessary.
- ///
- /// # Returns
- /// An immutable reference to the inner value `T`.
+ fn force_init(&mut self) -> &mut LazyInner<T> {
+ if matches!(&self.inner, LazyInner::Uninit(_)) {
+ // Replace with a temporary poison value so we can move the real factory out.
+ // SAFETY: if the factory panics, the poison value's `unreachable!()` will
+ // catch any subsequent access.
+ let poisoned = LazyInner::Uninit(Box::new(|| {
+ unreachable!("LazyRes poisoned during initialization")
+ }));
+ let old = std::mem::replace(&mut self.inner, poisoned);
+ match old {
+ LazyInner::Uninit(mut f) => {
+ self.inner = LazyInner::Init((f)());
+ }
+ // Already initialized — put it back
+ init @ LazyInner::Init(_) => {
+ self.inner = init;
+ }
+ }
+ }
+ &mut self.inner
+ }
+
+ /// Returns an immutable reference to the inner value,
+ /// calling the initializer on first access.
pub fn get_ref(&mut self) -> &T {
- if self.inner.is_none() {
- self.inner = Some((self.init_fn)());
+ self.force_init();
+ match &self.inner {
+ LazyInner::Init(t) => t,
+ _ => unreachable!(),
}
- self.inner.as_ref().unwrap()
}
- /// Returns a mutable reference to the inner value, calling the initializer if necessary.
- ///
- /// # Returns
- /// A mutable reference to the inner value `T`.
+ /// Returns a mutable reference to the inner value,
+ /// calling the initializer on first access.
pub fn get_mut(&mut self) -> &mut T {
- if self.inner.is_none() {
- self.inner = Some((self.init_fn)());
+ self.force_init();
+ match &mut self.inner {
+ LazyInner::Init(t) => t,
+ _ => unreachable!(),
}
- self.inner.as_mut().unwrap()
}
- /// Resets the lazy resource by taking and returning the inner value; on next access, re-initialization occurs.
- ///
- /// # Returns
- /// Returns the previously initialized value if any, otherwise `None`.
- pub fn reset(&mut self) -> Option<T> {
- self.inner.take()
+ /// Returns a clone of the inner value, calling the initializer if necessary.
+ pub fn get_clone(&mut self) -> T
+ where
+ T: Clone,
+ {
+ self.get_ref().clone()
}
- /// Returns a clone of the inner value, calling the initializer if necessary.
+ /// Consumes the lazy resource and returns the inner value, if initialized.
///
- /// # Returns
- /// A clone of type `T`.
- pub fn get_clone(&mut self) -> T {
- self.get_ref().clone()
+ /// Unlike `reset()`, this **drops** the lazy wrapper entirely.
+ /// If you need to re-initialize, just construct a new `LazyRes::new(f)`.
+ pub fn into_inner(self) -> Option<T> {
+ match self.inner {
+ LazyInner::Init(t) => Some(t),
+ LazyInner::Uninit(_) => None,
+ }
+ }
+}
+
+impl<T: Send + Sync + 'static> Default for LazyRes<T>
+where
+ T: Default,
+{
+ /// Creates an uninitialized `LazyRes<T>` whose initializer returns `T::default()`.
+ fn default() -> Self {
+ Self::new(|| T::default())
}
}
-impl<T: Default + Send + Sync + Clone + 'static> From<T> for LazyRes<T> {
- /// Creates a `LazyRes<T>` from an existing value (already initialized), with the initializer set to `T::default()`.
+impl<T: Send + Sync + 'static> From<T> for LazyRes<T> {
+ /// Creates a `LazyRes<T>` from an already-initialized value.
fn from(value: T) -> Self {
Self {
- inner: Some(value),
- init_fn: Box::new(|| T::default()),
+ inner: LazyInner::Init(value),
}
}
}
-impl<T: Default + Send + Sync + Clone + 'static> LazyRes<T> {
+impl<T: Send + Sync + 'static> LazyRes<T> {
/// Creates a lazily initialized resource using `T::default()` as the initializer.
- pub fn lazy_default() -> Self {
+ pub fn lazy_default() -> Self
+ where
+ T: Default,
+ {
Self::default()
}
- /// Creates a lazily initialized resource with a custom initializer function.
+ /// Creates a lazily initialized resource with a custom initializer.
///
/// Same as `LazyRes::new`.
pub fn lazy_init(f: impl FnMut() -> T + Send + Sync + 'static) -> Self {
@@ -100,34 +127,48 @@ impl<T: Default + Send + Sync + Clone + 'static> LazyRes<T> {
}
}
-/// Provides convenience methods for types implementing `Default + Send + Sync + Clone + 'static`,
-/// allowing them to easily create a corresponding `LazyRes<T>`.
-pub trait LazyInit: Default + Send + Sync + Clone + 'static {
+/// Provides convenience methods for types, allowing them to easily
+/// create a corresponding `LazyRes<T>`.
+pub trait LazyInit: Send + Sync + 'static {
/// Creates a lazily initialized resource for this type using `Default` as the initializer.
- fn lazy_default() -> LazyRes<Self> {
+ fn lazy_default() -> LazyRes<Self>
+ where
+ Self: Default,
+ {
LazyRes::default()
}
- /// Creates a lazily initialized resource for this type with a custom initializer function.
- fn lazy_init(f: impl FnMut() -> Self + Send + Sync + 'static) -> LazyRes<Self> {
+ /// Creates a lazily initialized resource for this type with a custom initializer.
+ fn lazy_init(f: impl FnMut() -> Self + Send + Sync + 'static) -> LazyRes<Self>
+ where
+ Self: Sized,
+ {
LazyRes::new(f)
}
}
-impl<T: Default + Send + Sync + Clone + 'static> LazyInit for T {}
+impl<T: Send + Sync + 'static> LazyInit for T {}
-impl<T: Default + Send + Sync + Clone + 'static> ResourceMarker for LazyRes<T> {
- /// Clones the lazy resource. The cloned resource retains any initialized value, but the initializer is reset to `T::default()`.
+impl<T: Send + Sync + 'static> ResourceMarker for LazyRes<T>
+where
+ T: Default + Clone,
+{
+ /// Clones the lazy resource. The cloned resource retains any initialized value,
+ /// but the initializer is reset to `T::default()`.
fn res_clone(&self) -> Self {
- Self {
- inner: self.inner.clone(),
- // The original initializer is not preserved on clone
- init_fn: Box::new(|| T::default()),
+ match &self.inner {
+ LazyInner::Init(t) => Self {
+ inner: LazyInner::Init(t.clone()),
+ },
+ LazyInner::Uninit(_) => Self::default(),
}
}
/// Returns a default lazy resource (uninitialized, using `T::default()` as the initializer).
- fn res_default() -> Self {
+ fn res_default() -> Self
+ where
+ T: Default,
+ {
Self::default()
}