Hi everyone! Can someone point me in the general direction of an idiomatic way to build a static, constant map between some trivial enums and a set of constant structs?
Specifically, suppose I have this:
pub enum OldArch {
Z80 = 0,
M6809 = 1,
M6502 = 2,
M68K = 3,
}
and I'd like to map constant values of that to (references to) constant values of this:
pub struct OldArchParam {
year: u32,
addr_bus_width: u32,
floats_are_also_involved: f64,
}
(All that is just for illustration, I'm not actually writing a program that handles old architectures, but tl;dr I want to map a bunch of enum variants to a bunch of structs whose members are constant integer and floats).
Since both the keys (the enum OldArch
) and the values (struct OldArchData
) of the map are constant and known at compile time, I'd like to have a constant, static map (of some sort -- I'm not specifically looking for a hash map, anything that I can get random access in constant-ish time and safety guarantees with is okay).
Based on stuff that was previously discussed here and elsewhere on the Interwebs, I know about enum_map
but as far as I can tell it can't build the map at compile time. phf
can't quite do that, either, since enum OldArch
is not a type that it can hash at compile-time. I guess I could write my own macro which would "translate" my enum IDs into type-suffixed integers (which phi
can hash) but it seems a little... involved?
The closest I've come is something like this:
impl OldArch {
const fn data(self) -> &'static OldArchParam {
match self {
OldArch::Z80 => &OldArchParam { year: 1976, addr_bus_width: 16, floats_are_also_involved: 1.0 },
OldArch::M6809 => &OldArchParam { year: 1978, addr_bus_width: 16, floats_are_also_involved: 2.4 },
// Some cases omitted for brevity
}
}
}
...
let p = OldArch::data(OldArch::Z80);
If I got the output right, that compiles down to calling a function (OldArch::data
) that makes room for a pointer on the stack, then uses an inline dispatch table to jump to a sequence which moves a pointer to the correct constant OldArchData
instance into the pointer it made room for on the stack, and returns that pointer.
I would expect that, since I'm mapping between values that are constant and known at compile time, I should be able to say something like this:
let p = <Some magical incantation that gets me a reference to a static constant `struct OldArchData`>(OldArch::Z80);
which rustc
would translate to just mov
-ing the right value into p
, without any intermediary dispatch tables being involved anywhere, and without the stack being involved for anything other than allocating p
.
Am I wrong in this assumption, or am I missing a cool feature? Alternatively, am I actually misreading the compiler output (here: Compiler Explorer -- I'm reasonably fluent in assembly language for a bunch of architectures but amd64 is actually not one of them, and by the way do you know a bar where I can pay with hipster points?)
(I'm obviously open to thinking outside the C box here, which I am very likely doing -- perhaps I'm starting with the wrong abstraction here by using an enum
like that?)
Thank you very much for your help!