My misunderstand of "const generic"

When I heard about "const generic" I was thinking about something like

const<T> EMPTY_OPTION: Option<T> = None;
...
let ref_to_option:&'static _ = &EMPTY_OPTION::<u32>; 
//so we can freely return this to callers without worrying about lifetimes... 

Not

fn add_multiple_times<const N:u8>(v: u32, to_add: u8) -> u32 {...};
...
let result = add_multiple_times::<10>(8, 9);

I know it actually means the later and it is going to land soon; but what about the former? Are there any plans?

I'd personally call the former "generic constants". As far as I'm aware nobody has ever proposed this, let alone implemented it.

4 Likes

We've definitely asked for generic constants statics before.

1 Like

Well I've never seen it at least. I obviously can't speak for what I haven't seen.

I think no one will blame you for this; but we would like to see discussions about the feature and what would be the technical issues behind it.

This feature is commonly exists in other languages with generics, as far as I know the list includs C++ (as a class level static variable) and C# (commonly understood as "type dictionary").

I would love to get generic constants, but I'm getting by with associated constants.

Generic constants can be emulated with a generic struct (that is never instantiated), and an associated constant of that struct: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=1d7d7eee5c6472c3288671a97beb9d64

use std::marker::PhantomData;

pub struct EMPTY_OPTION<T>(PhantomData<T>);

impl<T> EMPTY_OPTION<T> {
    pub const V: Option<T> = None;
}

fn main(){
    let ref_to_option:&'static _ = &EMPTY_OPTION::<u32>::V; 
}

While this is some boilerplate, I don't need generic constants often enough for it to be a problem.

5 Likes

🙋

8 Likes

Talking about the RFC: https://github.com/comex/rfcs/blob/master/text/0000-generic-const-static.md

I'll mention that the motivation in here is partially covered by associated constants today:

The most important benefit is the ability to generate a separate static allocation in the binary for each used set of type parameters. The use case for scalar-typed generic consts somewhat overlaps with const fns, but when a static address is needed, Rust currently provides no alternative or workaround. (In particular, consts and statics inside generic fns cannot refer to the outer fn's type parameters, nor can associated consts inside traits refer to trait generic parameters or to Self. Actually, you can technically do it with asm!...)

since associated constants can now refer to the generic parameters of the impl block or trait (including Self), and can construct static references (so long as the type doesn't contain internal mutability) .

Also, the example that constructs HashKeyData can be written using associated constants: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=498925e5febae16e77c7c615c99582cf

The V associated constant is a &'static HashKeyData to have the same effect as referencing a generic static, except that it doesn't guarantee a single address for all instances of HKD::<Foo>::V.
As I understand it, HKD::<Bar>::V accross compilation units can produce different addresses.

Generic constants would have the advantage that they'd be nicer to use than associated constants for this purpose.

Generic statics would have the advantages that they guarantee all instances of HKD::<u8> (where HKD is a generic static) have the same address, and would allow taking static references to an internally mutable type.

1 Like

Ok. I read that discussion thread and my impression is that the language team decided to think about it after the "const generic" being landed. So it would not be too long to re-open it. Thanks.

I found your previous proposal.

It is in "open" status, same as the actual "const generic" feature tracking issue.

So yes, things are going forward. Thanks for the team!

1 Like

No new language feature needed for the example in your post, the following:

pub fn empty<'a, T: 'a>() -> &'a Option<T> { &None }

compiles today and allows 'static if T: 'static.

That's static promotion, and it doesn't work if the expression has function calls (to const fn), or contains interior mutability (any type that recursively contains an UnsafeCell field).

The calling const fn part will work with inline const once it's stabilized, but interior mutability requires generic statics.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=09a460bd2a3e6ffcd531a196ff5140d8

error[E0515]: cannot return reference to temporary value
 --> src/lib.rs:7:41
  |
7 | pub fn empty() -> &'static Mutex<u32> { &M }
  |                                         ^-
  |                                         ||
  |                                         |temporary value created here
  |                                         returns a reference to data owned by the current function

error[E0515]: cannot return reference to temporary value
 --> src/lib.rs:9:40
  |
9 | pub fn returns_two() -> &'static u32 { &two() }
  |                                        ^-----
  |                                        ||
  |                                        |temporary value created here
  |                                        returns a reference to data owned by the current function

Generic statics

Generic statics would allow you to get static references to types with interior mutability, so this would work:

static INSTANCES<T>: AtomicU64 = AtomicU64::new(0);

this static would allow counting the amount of instances of any type, without manually creating different statics for each.

1 Like

It's already possible to achieve exactly what your first example does TBH. The following is perfectly valid, for example:

use std::marker::PhantomData;

struct Consts<'a, T> {
    phantom: PhantomData<&'a T>,
}

impl<T> Consts<'_, T> {
    const EMPTY_OPTION: Option<T> = None;
}

fn main() {
    let ref_to_option: &'static _ = &Consts::<u32>::EMPTY_OPTION;
}

I do agree that being able to write generic constants more "directly" would be nice in some cases though.

Edit: I just realized that someone else had already provided an extremely similar example... gets the point across even more for OP now I guess, haha.

1 Like