I'm generating unique IDs in my project by incrementing an atomic integer. This works fine at runtime but I would like to generate some global static unique IDs too, which is a problem because fetch_add is not a const function (presumably because it mutates the atomic):
static COUNTER: AtomicU64 = AtomicU64::new(0);
fn new_id() -> u64 {
COUNTER.fetch_add(1, Ordering::Relaxed)
}
// This doesn't work because new_id() is not const
static MY_ID: u64 = new_id();
Is there any way to make this work, other than lazy initialization?
(I don't need the IDs to be predicatable nor unique between runs, they only need to be unique for the duration of my program's runtime.)
I think the easiest way is to use a pointer to static as such ID. IIRC the language guarantees that each non-ZST static has a unique address, even if stored values are the same (though it's worth to check the language reference). For example, see this snippet: Rust Playground
Thanks for the tip, however, this would only work if I defined all of the constants in one place. I'd like to be able to do this across multiple files. In fact, I'd like to be able to provide this as a library function for others to use in their own files just like the new_id() function.
Hmm, okay, that's interesting. I think I see how I could make this work, the only problem is that I'd like to package this so that the users of my library can use it in an intuitive and safe way. I think I'd have to make it a macro which defines a static u8 and then takes its address and turns it into the ID. I wonder if there's any way that the u8 could be optimized away since it's not really used and the address get somehow recycled?
But that's the thing, you can. They just need to be const. And I don't see any reason why this couldn't be executed at compile time in principle. Although I do get the whole "there's state to carry around" argument. I'm just looking for some loophole that would let me get around this.
Right, but I'd like to create more than one ID, and I don't want to manually check that I've assigned them all different values, surely there's some way the compiler could do that for me? Plus I'd like to make this a library function that others can use and they're not necessarily going to be able to look at all other pieces of the project where other IDs are generated.
const functions only ever get evaluated once per argument. They cannot change state. So any const id() function will always return the same ID on each call.
You cannot do this with macros either if you want to use it in different modules, since macros cannot store state across invocations. They get evaluated and then "forget" everything that happened.
But what's the final goal? If history is any indication then you would find it, then language developers will plug the hole, then you would find another hole and then it would be plugged, too, and so on. Google "stateful metaprogramming C++" and you'll find enough articles that follow that journey.
If you want to keep your intellectual muscles sharp then there are more mundane tasks to apply it to.
When I said “loophole” I really meant a well-defined feature which was perhaps not intended for this use case but it could be used to achieve my goal. I already got one good suggestion from @newpavlov, this seems like my best bet so far.
That's exactly what happened with C++: people were finding wordings that allowed them to do what you want, then C++ committee decreed that it was an error in the reference, then new standard permitted the other way to achieve the same thing, rinse and repeat.
That's not the same thing at all. Taking an address of a static seems like a perfectly well defined operation to me and I see no reason why anyone should be unhappy about it being permitted by the language. Yet it generates a unique integer at compile time. You could probably do something similar by combining the file name & line number (except that it seems a little less elegant). I imagine there could be other perfectly reasonable and well-defined tricks that I could use and if anyone has any suggestions I'd be happy to hear them .
It's permitted by the language, but it's not clear if it would be supported by the environment you care about. E.g. on Windows you couldn't take address of a static from DLL.
On the other hand the fact that it's dictated by environment means that it's harder to change it that the language rules.
It doesn't. Unique address is generated by linker, not compiler. In C++ this lead to the question of “are these two addresses the same or different” having different answers depending on which compiler and which exact approach you are talking about. In runtime everything works like one would assume, in compile-time… story is very non-trivial. I'm not sure how reliably it works with Rust.
So basically a proc macro that generates a random ID on every invocation right? I guess that could work too although I should probably upgrade my IDs to u128 to avoid collisions.