The behavior of static varible in different scopes is inconsistent

See the following code:

pub trait Get {
    fn get(key: &'static str) -> &'static str {
        key
    }
}

use once_cell::sync::OnceCell;
struct A;
struct B;

impl Get for A{}
impl Get for B{}

fn test_once_cell<T: Get>(key: &'static str, _: T) -> &'static str{
    static ENV_TEST: OnceCell<&'static str> = OnceCell::new();
    ENV_TEST.get_or_init(|| {
        T::get(key)
    })
}

fn main() {
    
    let a = {
        static ENV_TEST: OnceCell<&'static str> = OnceCell::new();
        ENV_TEST.get_or_init(|| {
            "OnceCell_Once"
        })
    };
    let b = {
        static ENV_TEST: OnceCell<&'static str> = OnceCell::new();
        ENV_TEST.get_or_init(|| {
            "OnceCell_Twice"
        })
    };
    let c = test_once_cell("OnceCell_Once", A);
    let d = test_once_cell("OnceCell_Twice", B);
    println!{"{}, {}", *a, *b};
    println!{"{}, {}", c, d};
}

The output is:

OnceCell_Once, OnceCell_Twice
OnceCell_Once, OnceCell_Once

That is to say, generic functions are not expanded into two seeparate functions by the compiler, am I right? If so, it would be very confusing, after all, I use c++ before.

Generic functions do expand into multiple copies (monomorphization). The difference is the static variable is actually shared between all copied. IIRC basically static variables and free functions can be defined inside functions. But that only to restricts the scope name, otherwise, in every way, they act just like if they were defined outside the function.

This also means, for example, that you cannot use generic parameters from the outer function in the nested static variable or free function

3 Likes

Generic functions generate one function per type, not per use. In this case, the second call uses the same once cell that was initialized on the first call.

Edit never mind, I'm wrong.

Here's a light modification where you can see they are separate functions (using the same ENV_TEST static).

Thank you, I understand the rules of generics functions. And, how to understand the behavior of a and b in main function?

The document is here.

https://doc.rust-lang.org/stable/reference/items.html

The meaning of these scoped items is the same as if the item was declared outside the scope — it is still a static item — except that the item's path name within the module namespace is qualified by the name of the enclosing item, or is private to the enclosing item (in the case of functions).

Location of static item only affects its naming scope. Except for that, it behaves the same as if it were in the module root.

1 Like

I see that, thank you!

Follow-up question. Is there, or will there ever be, any way to create a static that is generic?

There were various discussions about that, the TL;DR is that's problematic, but may happen IIRC. E.g.:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.