#singleton #generic #static #global

generic_singleton

A library crate allowing for generic singleton patterns

10 releases

Uses new Rust 2024

0.5.3 Apr 1, 2026
0.5.2 Mar 28, 2026
0.5.1 May 20, 2025
0.5.0 Jun 8, 2023
0.3.2 Nov 23, 2022

#88 in Memory management

Download history 3305/week @ 2026-01-23 3570/week @ 2026-01-30 3372/week @ 2026-02-06 4079/week @ 2026-02-13 5414/week @ 2026-02-20 4984/week @ 2026-02-27 5352/week @ 2026-03-06 6031/week @ 2026-03-13 7070/week @ 2026-03-20 6399/week @ 2026-03-27 7448/week @ 2026-04-03 5976/week @ 2026-04-10 6008/week @ 2026-04-17 6473/week @ 2026-04-24 7128/week @ 2026-05-01 7125/week @ 2026-05-08

27,670 downloads per month
Used in 25 crates (2 directly)

MIT/Apache

17KB
174 lines

generic_singleton

crates.io Documentation

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 anymap3 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 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

~345–550KB
~11K SLoC