Struct MaybeOnce

Source
pub struct MaybeOnce<T> { /* private fields */ }
Expand description

A MaybeOnce object is a variation of the OnceLock object that keeps track of the number of references to the internal data and drops it every time the references counter goes to 0. When the data is accessed, it will be created if it does not exist, or it will recreated if it was previously dropped.

This object is to be used to inizialize a shared resource that must be dropped when it is no longer used or when the process terminates. This mechanism is used as a workaround for the fact that rust does not drop static items at the end of the program.

This object is to be used to inizialize a shared resource that must be dropped when it is no longer used or when the process terminates. A typical example is to initialize an expensive object, for example to start a docker container, to be used by a set of integration tests, with the guarantee that it is properly dropped when the tests terminates.

Example:

mod test {

    use std::sync::OnceLock;
    use maybe_once::blocking::{Data, MaybeOnce};


/// A data initializer function. This can be called more than once.
/// If everything goes as expected, it should only be called once.
fn init() -> String {
    // Expensive initialization logic here.
    // For example, you can start here a docker container (e.g. by using testcontainers),
    // when there will be no more references to the data, 
    // the data will be dropped and the container will be stopped.
    "hello".to_string()    
}

/// A function that holds a static reference to the `MaybeOnce` object 
/// and returns a `Data` object.
pub fn data(serial: bool) -> Data<'static, String> {
    static DATA: OnceLock<MaybeOnce<String>> = OnceLock::new();
    DATA.get_or_init(|| MaybeOnce::new(|| init()))
        .data(serial)
}

    /// This test, and all the others, uses the data function to access the shared data.
    /// The same data instance is shared between all the threads exactly like OnceLock does,
    /// but when the all tests finish, the data will be dropped before the process terminates.
    #[test]
    fn test1() {
        let data = data(false);
        println!("{}", *data);
    }

    #[test]
    fn test2() {
        let data = data(false);
        println!("{}", *data);
    }

    #[test]
    fn test3() {
        let data = data(false);
        println!("{}", *data);
    }

}

Implementations§

Source§

impl<T> MaybeOnce<T>

Source

pub fn new(init: fn() -> T) -> Self

Creates a new MaybeOnce object with the given init function.

init is a function that creates a new T object. It is lazily called the first time data is called and every time after the data is dropped.

The returned MaybeOnce object is then used to access the shared data with the data method.

Source

pub fn data(&self, serial: bool) -> Data<'_, T>

This function returns a Data object, which allows you to access the shared data.

The serial parameter allows you to control whether the data is accessed in a serial or parallel manner. If serial is true, the data will be accessed in a serial manner, meaning that no other thread can access the data until the returned Data is dropped. If serial is false, the data will be accessed in a parallel manner, meaning that any number of threads can access the data at the same time.

The returned Data object implements Deref and AsRef, so you can use it like a reference.

The Data object also implements Drop, so when it goes out of scope, the lock is released.

Auto Trait Implementations§

§

impl<T> Freeze for MaybeOnce<T>

§

impl<T> !RefUnwindSafe for MaybeOnce<T>

§

impl<T> Send for MaybeOnce<T>
where T: Sync + Send,

§

impl<T> Sync for MaybeOnce<T>
where T: Sync + Send,

§

impl<T> Unpin for MaybeOnce<T>

§

impl<T> !UnwindSafe for MaybeOnce<T>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.