How about "generic global variables"?


A generic struct Foo<T> may need a generic global variable associated with the same T instantiated. Something like

/// library code

static mut FOO<T>: Option<Vec<T>> = None;

And for all instantiations of FOO::<T> there exists a global variable of the type Option<Vec<T>>. For example:

/// client code

let i32s_inited = FOO::<i32>.is_some(); // type annotation
let first_str: String = FOO  // type inference
    .map( |foo| foo.first()
        .map( |s| s.clone() )
        .unwrap_or_default() )

will produce two global variables, as if they have been declared previously:

/// library code

// not valid identifiers, just for demonstration
static mut FOO::<i32> : Option<Vec<i32>> = None;
static mut FOO::<String> : Option<Vec<String>> = None;

Since it’s not valid Rust syntax right now, we have to use trait as alternative to achieve similar result:

/// library code

pub trait GlobalFoo where Self: Sized {
    fn foo() -> &'static mut Option<Vec<Self>> {

pub struct Foo<T> { /* omitted */ }

impl<T> Foo<T> where T: 'static + GlobalFoo
    fn using_generic_global( &self ) {
        let foo = T::foo();
        // omitted

/// client code

static mut FOO_I32: Option<Vec<i32>> = None;
static mut FOO_STRING: Option<Vec<String>> = None;

impl GlobalFoo for i32 {
    fn foo() -> &'static mut Option<Vec<i32>> {
        &mut FOO_I32

impl GlobalFoo for String {
    fn foo() -> &'static mut Option<Vec<String>> {
        &mut FOO_STRING

let i32_foo = Foo::<i32>::new();

let string_foo = Foo::<String>::new();

The alternative method is more verbose.

So, is it worth introducing “generic global variables”?


I have wondered about associated statics before, something like

struct Foo<T>(Option<T>);

impl<T> Foo<T> {
    static FOO: Self = Foo(None);

I’m not sure if there’s some reason this wouldn’t work though. First question is when do they get monomorphised? I think delaying it till all variants are collected in the binary should work.


I’ve had this exact issue in my current project. I have an interning system that’s completely transparent… except that you have to run this macro for every type you want to use it with because I can’t have a monomorphised static. That means exposing the macro, plus all the internal types and interfaces. Blech.

Plus consts, while we’re at it. I had a macro-based RTTI system in stable Rust over a year (?) ago whose sole problem was that it needed monomorphised consts to not be hideously inefficient.

Actually, that reminds me: one nice thing D had was that you could have bare templates. Not templated classes, just templates. The closest approximation in Rust would be generic modules. You could shove anything you wanted in a template, and it would Just Work™. Come to think of it, there’s still a few things Rust could stand to learn from D, but that’s getting off-topic.


Had this exact issue with my event bus crate. never used it since.


I had this issue in my trees crate :slight_smile:


(btw I don’t recommend returning &mut but that’s my opinion. it means any crate can reset the variable by calling the function and setting it to whatever. use an & and inner Mutex)


I don’t bother writing perfect interface in the example code :slight_smile: , so It’s just for demonstration.

And in my case, the global variables are not for public use, only struct Foo<T> has access to them.


static mut is still very problematic when encapsulated by privacy. For starters, you need to enforce synchronization, since other parts of the application may launch additional threads that call into the encapsulating code (so you’d have unsynchronized mutation). Furthermore, even if only a single thread is involved, code using static mut is almost certainly not reentrant because it’s UB to hold two &mut to the at overlapping times (see also

Leaving static mut aside, this observation hits at the key problem I see with implementing generic statics:

Because we still (unfortunately) support the dylib crate type, there is not necessarily a single place where all monomorphizations are known, and as far as I know there’s no cross platform way to merge duplicate statics at the linking stage. So we can’t actually guarantee that there is precisely one FOO::<i32> that all code agrees on.

Note that it’s possible (with some run time overhead) to use TypeId and Box<Any> to collect “one value per type” in a single static. See for example, though I think that’s unmaintained and I have not audited the implementation.


why not generate functions? it’s still less overhead than AnyMap or w/e, but allows runtime monomorphization, I think.


I’m not sure how useful this is to what you want, but we do have “generic consts” if you abuse the trait system:

trait MyConst {
    const VAL: T;

impl<T> MyConst for T {
    const VAL: T = ..;

<T as MyConst>::VAL

Of course, this only works because consts only live in the compiler’s imagination and don’t actually need to be monomorphized…


Thanks for so many useful information.

No guaranteed monomorphizations of generic statics is really the deal breaker, which rules out declaring generic static in library code as an acceptable option.

Is it ok to resort to the alternative of declaring normal statics in client code and using trait system for simulation, if global variables are synchronized and encapsulated in such a way eliminating recursive function calls( since I know all the paths to access to these globals in my library )?

Using typemap or similar crates with some affordable run time overhead seems to be an acceptable option. But it just packed many global variables into one single global variable, the map itself. Quoting from typemap’s example code in its README:

    let mut map = TypeMap::new();
    assert_eq!(*map.get::<KeyType>().unwrap(), Value(42));

The library users are not allowed to access to map since it’s an implementation detail.