Skip to content

Commit

Permalink
Added support for "failable" and "infailable" default initializers.
Browse files Browse the repository at this point in the history
  • Loading branch information
dEajL3kA committed Jan 27, 2023
1 parent 8816dc4 commit 84d1e8c
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 46 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "lazy_rc"
version = "0.1.2"
version = "0.1.3"
edition = "2021"
license-file = "LICENSE"
description = "Provides implementations of Rc<T> and Arc<T> with lazy initialization."
Expand Down
70 changes: 53 additions & 17 deletions src/lazy_arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,13 @@ use std::io::{Result as IoResult};
use std::sync::{Arc, RwLock};

use crate::InitError;
use crate::utils::{or_init_with, or_try_init_with};

/// A default initializer for [`LazyArc<T>`](crate::LazyArc)
type DefaultInit<T> = dyn Fn() -> IoResult<T> + Sync;
use crate::utils::{DefaultInit, or_init_with, or_try_init_with};

/// A thread-safe reference-counting pointer, akin to
/// [`Arc<T>`](std::sync::Arc), but with ***lazy*** initialization
pub struct LazyArc<T> {
inner: RwLock<Option<Arc<T>>>,
default_init: Option<Box<DefaultInit<T>>>,
default_init: DefaultInit<T>,
}

impl<T> LazyArc<T> {
Expand All @@ -28,7 +25,7 @@ impl<T> LazyArc<T> {
pub const fn empty() -> Self {
Self {
inner: RwLock::new(None),
default_init: None,
default_init: DefaultInit::None,
}
}

Expand All @@ -38,12 +35,27 @@ impl<T> LazyArc<T> {
/// The "inner" value will be [initialized](Self::or_init_with()) on first
/// access. Default initialization *is* supported by this instance.
pub fn with_default_init<U>(default_init: U) -> Self
where
U: Fn() -> T + Sync + 'static,
{
Self {
inner: RwLock::new(None),
default_init: DefaultInit::Infailable(Box::new(default_init)),
}
}

/// Create a new `LazyArc<T>` that is initially *empty* and that contains
/// the given failable *default* initializer.
///
/// The "inner" value will be [initialized](Self::or_init_with()) on first
/// access. Default initialization *is* supported by this instance.
pub fn with_failable_default_init<U>(default_init: U) -> Self
where
U: Fn() -> IoResult<T> + Sync + 'static,
{
Self {
inner: RwLock::new(None),
default_init: Some(Box::new(default_init)),
default_init: DefaultInit::Failable(Box::new(default_init)),
}
}

Expand All @@ -52,6 +64,24 @@ impl<T> LazyArc<T> {
self.inner.read().map(|val| val.is_some()).unwrap_or(false)
}

/// Returns a pointer to the existing "inner" value, or tries to initialize
/// the value right now.
///
/// If and only if the "inner" value is **not** initialized yet, the
/// "inner" value is set to the return value of the *default* initializer
/// and a new `Arc<T>` pointer to the "inner" value is returned. The
/// default initializer **must** be *infailable*, otherwise use
/// [`or_try_init()`](Self::or_try_init)!
///
/// Warning: This function [panics](mod@std::panic), if **no** *default*
/// initializer is available, or of the default initializer is *failable*!
pub fn or_init(&self) -> Arc<T> {
match &self.default_init {
DefaultInit::Infailable(init) => or_init_with(self.inner.write().unwrap(), || Arc::new(init())),
_ => panic!("No infailable default initializer!"),
}
}

/// Returns a pointer to the existing "inner" value, or tries to initialize
/// the value right now.
///
Expand All @@ -63,12 +93,13 @@ impl<T> LazyArc<T> {
/// If **no** *default* initializer is available, an error of type
/// [`NoDefaultInitializer`](crate::InitError) is returned.
pub fn or_try_init(&self) -> Result<Arc<T>, InitError> {
match self.default_init.as_ref() {
Some(init) => match or_try_init_with(self.inner.write().unwrap(), || init().map(Arc::new)) {
match &self.default_init {
DefaultInit::Infailable(init) => Ok(or_init_with(self.inner.write().unwrap(), || Arc::new(init()))),
DefaultInit::Failable(init) => match or_try_init_with(self.inner.write().unwrap(), || init().map(Arc::new)) {
Ok(value) => Ok(value),
Err(error) => Err(InitError::Failed(error)),
}
None => Err(InitError::NoDefaultInitializer)
},
DefaultInit::None => Err(InitError::NoDefaultInitializer)
}
}

Expand Down Expand Up @@ -108,6 +139,11 @@ impl<T> LazyArc<T> {
}
}

/// An alias for the [`or_init()`](Self::or_init) function.
pub fn unwrap(&self) -> Arc<T> {
self.or_init()
}

/// Applies function `map_fn()` to the "inner", if already initialized.
///
/// If and only if the "inner" value already *is* initialize, the function
Expand Down Expand Up @@ -155,7 +191,7 @@ impl <T> From<T> for LazyArc<T> {
fn from(value: T) -> Self {
Self {
inner: RwLock::new(Some(Arc::new(value))),
default_init: None,
default_init: DefaultInit::None,
}
}
}
Expand All @@ -168,7 +204,7 @@ where
fn from(value: &T) -> Self {
Self {
inner: RwLock::new(Some(Arc::new(value.clone()))),
default_init: None,
default_init: DefaultInit::None,
}
}
}
Expand All @@ -178,7 +214,7 @@ impl <T> From<Arc<T>> for LazyArc<T> {
fn from(value: Arc<T>) -> Self {
Self {
inner: RwLock::new(Some(value)),
default_init: None,
default_init: DefaultInit::None,
}
}
}
Expand All @@ -188,7 +224,7 @@ impl <T> From<&Arc<T>> for LazyArc<T> {
fn from(value: &Arc<T>) -> Self {
Self {
inner: RwLock::new(Some(value.clone())),
default_init: None,
default_init: DefaultInit::None,
}
}
}
Expand All @@ -210,8 +246,8 @@ impl<T> Clone for LazyArc<T> {

impl<T> Debug for LazyArc<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "LazyArc {{ default_init: {}, is_initialized: {} }}",
self.default_init.is_some(),
write!(f, "LazyArc {{ default_init: {:?}, is_initialized: {:?} }}",
self.default_init,
self.inner.read().unwrap().is_some())
}
}
72 changes: 54 additions & 18 deletions src/lazy_rc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,13 @@ use std::rc::Rc;
use std::cell::RefCell;

use crate::InitError;
use crate::utils::{or_init_with, or_try_init_with};

/// A default initializer for [`LazyRc<T>`](crate::LazyRc)
type DefaultInit<T> = dyn Fn() -> IoResult<T>;
use crate::utils::{DefaultInit, or_init_with, or_try_init_with};

/// A single-threaded reference-counting pointer, akin to
/// [`Rc<T>`](std::rc::Rc), but with ***lazy*** initialization
pub struct LazyRc<T> {
inner: RefCell<Option<Rc<T>>>,
default_init: Option<Box<DefaultInit<T>>>,
default_init: DefaultInit<T>,
}

impl<T> LazyRc<T> {
Expand All @@ -29,7 +26,7 @@ impl<T> LazyRc<T> {
pub const fn empty() -> Self {
Self {
inner: RefCell::new(None),
default_init: None,
default_init: DefaultInit::None,
}
}

Expand All @@ -40,11 +37,26 @@ impl<T> LazyRc<T> {
/// access. Default initialization *is* supported by this instance.
pub fn with_default_init<U>(default_init: U) -> Self
where
U: Fn() -> IoResult<T> + 'static,
U: Fn() -> T + Sync + 'static,
{
Self {
inner: RefCell::new(None),
default_init: DefaultInit::Infailable(Box::new(default_init)),
}
}

/// Create a new `LazyRc<T>` that is initially *empty* and that contains
/// the given failable *default* initializer.
///
/// The "inner" value will be [initialized](Self::or_init_with()) on first
/// access. Default initialization *is* supported by this instance.
pub fn with_failable_default_init<U>(default_init: U) -> Self
where
U: Fn() -> IoResult<T> + Sync + 'static,
{
Self {
inner: RefCell::new(None),
default_init: Some(Box::new(default_init)),
default_init: DefaultInit::Failable(Box::new(default_init)),
}
}

Expand All @@ -53,6 +65,24 @@ impl<T> LazyRc<T> {
self.inner.borrow().is_some()
}

/// Returns a pointer to the existing "inner" value, or tries to initialize
/// the value right now.
///
/// If and only if the "inner" value is **not** initialized yet, the
/// "inner" value is set to the return value of the *default* initializer
/// and a new `Rc<T>` pointer to the "inner" value is returned. The
/// default initializer **must** be *infailable*, otherwise use
/// [`or_try_init()`](Self::or_try_init)!
///
/// Warning: This function [panics](mod@std::panic), if **no** *default*
/// initializer is available, or of the default initializer is *failable*!
pub fn or_init(&self) -> Rc<T> {
match &self.default_init {
DefaultInit::Infailable(init) => or_init_with(self.inner.borrow_mut(), || Rc::new(init())),
_ => panic!("No infailable default initializer!"),
}
}

/// Returns a pointer to the existing "inner" value, or tries to initialize
/// the value right now.
///
Expand All @@ -64,12 +94,13 @@ impl<T> LazyRc<T> {
/// If **no** *default* initializer is available, an error of type
/// [`NoDefaultInitializer`](crate::InitError) is returned.
pub fn or_try_init(&self) -> Result<Rc<T>, InitError> {
match self.default_init.as_ref() {
Some(init) => match or_try_init_with(self.inner.borrow_mut(), || init().map(Rc::new)) {
match &self.default_init {
DefaultInit::Infailable(init) => Ok(or_init_with(self.inner.borrow_mut(), || Rc::new(init()))),
DefaultInit::Failable(init) => match or_try_init_with(self.inner.borrow_mut(), || init().map(Rc::new)) {
Ok(value) => Ok(value),
Err(error) => Err(InitError::Failed(error)),
}
None => Err(InitError::NoDefaultInitializer)
},
DefaultInit::None => Err(InitError::NoDefaultInitializer)
}
}

Expand Down Expand Up @@ -103,6 +134,11 @@ impl<T> LazyRc<T> {
or_try_init_with(self.inner.borrow_mut(), || init_fn().map(Rc::new))
}

/// An alias for the [`or_init()`](Self::or_init) function.
pub fn unwrap(&self) -> Rc<T> {
self.or_init()
}

/// Applies function `map_fn()` to the "inner", if already initialized.
///
/// If and only if the "inner" value already *is* initialize, the function
Expand Down Expand Up @@ -150,7 +186,7 @@ impl <T> From<T> for LazyRc<T> {
fn from(value: T) -> Self {
Self {
inner: RefCell::new(Some(Rc::new(value))),
default_init: None,
default_init: DefaultInit::None,
}
}
}
Expand All @@ -163,7 +199,7 @@ where
fn from(value: &T) -> Self {
Self {
inner: RefCell::new(Some(Rc::new(value.clone()))),
default_init: None,
default_init: DefaultInit::None,
}
}
}
Expand All @@ -173,7 +209,7 @@ impl <T> From<Rc<T>> for LazyRc<T> {
fn from(value: Rc<T>) -> Self {
Self {
inner: RefCell::new(Some(value)),
default_init: None,
default_init: DefaultInit::None,
}
}
}
Expand All @@ -183,7 +219,7 @@ impl <T> From<&Rc<T>> for LazyRc<T> {
fn from(value: &Rc<T>) -> Self {
Self {
inner: RefCell::new(Some(value.clone())),
default_init: None,
default_init: DefaultInit::None,
}
}
}
Expand All @@ -205,8 +241,8 @@ impl<T> Clone for LazyRc<T> {

impl<T> Debug for LazyRc<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "LazyRc {{ default_init: {}, is_initialized: {} }}",
self.default_init.is_some(),
write!(f, "LazyRc {{ default_init: {:?}, is_initialized: {:?} }}",
self.default_init,
self.inner.borrow().is_some())
}
}
41 changes: 31 additions & 10 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,31 @@
* This is free and unencumbered software released into the public domain.
*/
use std::error::Error;
use std::io::Error as IoError;
use std::fmt::Debug;
use std::io::{Error as IoError, Result as IoResult};
use std::fmt::Display;
use std::ops::DerefMut;

type FnInit<T> = dyn Fn() -> T + Sync;
type FnInitFailable<T> = dyn Fn() -> IoResult<T> + Sync;

/// A wrapper that optionally contains a (possibly failable) initializer.
pub enum DefaultInit<T> {
None,
Infailable(Box<FnInit<T>>),
Failable(Box<FnInitFailable<T>>),
}

/// An error that indicates that the initialization has failed.
#[derive(Debug)]
pub enum InitError {
/// Initialization failed, because **no** default initializer is available!
NoDefaultInitializer,
/// The initializer function has failed! The original error is forwarded as
/// "inner" value of this [`InitError`] variant.
Failed(IoError),
}

pub fn or_init_with<T, F>(mut inner: impl DerefMut<Target = Option<T>>, init_fn: F) -> T
where
T: Clone,
Expand Down Expand Up @@ -35,21 +56,21 @@ where
}
}

/// An error that indicates that the initialization has failed.
#[derive(Debug)]
pub enum InitError {
/// Initialization failed, because **no** default initializer is available!
NoDefaultInitializer,
/// The initializer function has failed! The original error is forwarded as
/// "inner" value of this [`InitError`] variant.
Failed(IoError),
impl<T> Debug for DefaultInit<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::None => write!(f, "None"),
Self::Infailable(_) => write!(f, "Infailable"),
Self::Failable(_) => write!(f, "Failable"),
}
}
}

impl Display for InitError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
InitError::NoDefaultInitializer => write!(f, "No default initializer available!"),
InitError::Failed(error) => error.fmt(f),
InitError::Failed(error) => Display::fmt(&error, f),
}
}
}
Expand Down

0 comments on commit 84d1e8c

Please sign in to comment.