7 unstable releases (3 breaking)

0.5.0 Jun 8, 2023
0.4.1 Jan 12, 2023
0.3.2 Nov 23, 2022
0.3.1 Sep 16, 2022
0.1.3 Sep 8, 2022

#2241 in Rust patterns

Download history 298/week @ 2024-07-22 94/week @ 2024-07-29 129/week @ 2024-08-05 170/week @ 2024-08-12 120/week @ 2024-08-19 209/week @ 2024-08-26 257/week @ 2024-09-02 235/week @ 2024-09-09 23/week @ 2024-09-16 188/week @ 2024-09-23 146/week @ 2024-09-30 113/week @ 2024-10-07 106/week @ 2024-10-14 46/week @ 2024-10-21 47/week @ 2024-10-28 48/week @ 2024-11-04

249 downloads per month
Used in 2 crates (via easyfft)

MIT/Apache

21KB
184 lines

generic_singleton

Rust does NOT monomorphize it's static generic items. This means you cannot use a generic static item in a generic function. You'll get the following error:

error[E0401]: can't use generic parameters from outer function

That's pretty frustrating when you want to write a singleton pattern that rely's on a generic parameter. This crate allows for this pattern with minimal runtime overhead.

generic_singleton uses anymap behind the scenes to store a map of each generic type. The first time you hit the get_or_init macro we initialize the singleton. Subsequent calls to get_or_init will retrieve the singleton from the map.

Example

use std::{ops::AddAssign, sync::RwLock};

use num_traits::{One, Zero};

fn generic_call_counter<T: Zero + One + Copy + AddAssign + Send + Sync + 'static>() -> T {
    let mut count = generic_singleton::get_or_init!(|| RwLock::new(T::zero())).write().unwrap();
    *count += T::one();
    *count
}

fn main() {
    // Works with usize
    assert_eq!(generic_call_counter::<usize>(), 1);
    assert_eq!(generic_call_counter::<usize>(), 2);
    assert_eq!(generic_call_counter::<usize>(), 3);

    // Works with i32
    assert_eq!(generic_call_counter::<i32>(), 1);
    assert_eq!(generic_call_counter::<i32>(), 2);
    assert_eq!(generic_call_counter::<i32>(), 3);

    // Works with f32
    assert_eq!(generic_call_counter::<f32>(), 1.0);
    assert_eq!(generic_call_counter::<f32>(), 2.0);
    assert_eq!(generic_call_counter::<f32>(), 3.0);
}

Thread Local variant

The example shown above has a drawback of requiring an RwLock to ensure synchronisation around the inner AnyMap. In single-threaded situations we can remove this lock and provide mutable references directly using the get_or_init_thread_local! macro. This comes at the cost of ergonomics, requiring you to express your logic in a closure rather than simply returning a reference.

Dependencies

~0.4–5MB
~12K SLoC