Skip to content

Different static variables for different instances of a generic function #2130

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
hajifkd opened this issue Aug 27, 2017 · 11 comments
Closed
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.

Comments

@hajifkd
Copy link

hajifkd commented Aug 27, 2017

Currently, different instances of a generic function have an identical static variable.

For example, the following code

fn foo<T>() {
    use std::sync::{Once, ONCE_INIT};

    static INIT: Once = ONCE_INIT;

    INIT.call_once(|| {
        // run initialization here
        println!("Called");
    });
}

fn main() {
    foo::<i64>();
    foo::<i64>();
    foo::<isize>();
}

calls println! just once. This is because the entities of the static variable INIT are the same for each different instance of the generic function foo.
(Well, actually I have asked this in stackoverflow, but I only got a workaround to use HashMap essentially. Using dynamical way to dispatch static code sounds a bit uncomfortable. )

I feel this behavior of generic functions and static variables is counter-intuitive since each instances of generic functions have different name in assembly and thus they are different function.
Of course, I agree that changing it loses compatibility and may not be good, so I would like to ask whether there is any room to add a new syntax or something to allow for different instances of a generic function to have different static variables. I feel this is important especially when we "translate" some C++ template code into Rust.

@eddyb
Copy link
Member

eddyb commented Aug 27, 2017

FWIW the Rust semantics are "if you can write outside a function, putting it inside a block expression will only change where you can refer to it by name and nothing else".

So you can embed entire modules in an expression - not only in functions but also array lengths, and they behave the same as outside, except name resolution can get a bit confusing in some cases.

@burdges
Copy link

burdges commented Aug 27, 2017

I'd imagine you want polymorphic/generic statics for this :

    ...
    struct OnceAndPhandom<T>(Once,PhandomData<T>);
    static INIT: OnceAndPhandom<T> = (ONCE_INIT,PhandomData);
    INIT.0.call_once(|| {
        ...

Also, the polymorphic/generic consts proposed in #1945 look useful too, ala

pub const OK<E>: Result<(),E> = Ok(());

I wonder if this could be merged with #1945 but even if not that might give you ideas about what the RFC should look like.

@hajifkd
Copy link
Author

hajifkd commented Aug 27, 2017

@eddyb Okay, I agree that if we treat T as just like a argument or a variable, then as you said this result is consistent.
But, considering assembly and that different instances of a generic function have different name, the above code looks similar to something like

fn foo1() {
    static A: i64 = 1i64;
    println!("{}", A);
}

fn foo2() {
    static A: i64 = 2i64;
    println!("{}", A);
}

, where two As are completely independent. Anyway, I believe some kind of static dispatch system is useful.

@burdges Generic statics indeed seems to solve my problem as you wrote although I have no idea what should happen in the following example:

fn foo<T, S>() {
    ...
    struct OnceAndPhandom<T>(Once,PhandomData<T>);
    static INIT: OnceAndPhandom<T> = (ONCE_INIT,PhandomData);
    INIT.0.call_once(|| {
        ...
}

fn main() {
    foo<i64, usize>();
    foo<i64, f64>();
}

@SimonSapin
Copy link
Contributor

This sounds like https://github.com/chris-morgan/anymap

@eddyb
Copy link
Member

eddyb commented Aug 27, 2017

Okay, I agree that if we treat T as just like a argument or a variable, then as you said this result is consistent.

All you have to do for consistency is to move anything that can be written outside a function, outside.

@sanmai-NL
Copy link

@hajifkd: Can this issue be closed now?

@hajifkd
Copy link
Author

hajifkd commented Oct 18, 2017

Okay. I'm sorry to bother you.

@hajifkd hajifkd closed this as completed Oct 18, 2017
@Centril Centril added the T-lang Relevant to the language team, which will review and decide on the RFC. label Feb 23, 2018
@jgarvin
Copy link

jgarvin commented Jul 31, 2021

AFAICT the lack of this feature means size specific global free lists in
Rust will always be slower than in C++, unless you predeclare explicitly all the sizes you intend to support (which once generics get involved becomes impossible). In C++, static generics let you declare there is a different free list for every ‘T<U>’ for all ‘U’ it is instantiated with, and generated code will directly refer to it. Rust code wanting this effect must go through a level of indirection using type ID as a key, and I don’t think there is any way for the compiler to optimize it out.

@nhtyy
Copy link

nhtyy commented Aug 4, 2024

I know this is many years later but is there an open issue for polymorphic statics? Im unable to find one. cc @burdges

@SOF3
Copy link

SOF3 commented Aug 8, 2024

Are there any existing places, or if it is possible at all, that static data that can be dynamically created and still shared between dependent crates without a single crate that owns the static field?

@SOF3
Copy link

SOF3 commented Aug 8, 2024

Also I don't know why you have a requirement to use statics on an arbitrary type in the first place, but if all such types requiring such a static field are known at a confined point, you can simply make it a trait:

#[derive(HasStatic)]
#[static(StaticType = init_expr)]
struct Foo;

// derives
const _: () = {
    static _STATIC: StaticType = init_expr;
    impl HasStatic<StaticType> for Foo {
        fn get(&self) -> &StaticType { &_STATIC }
    }
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

No branches or pull requests

9 participants