Declaring `&'static` without extern

Suppose I'm writing some low-level code that uses hardware registers (I am), and I want to have a global variable representing a set of hardware registers using some struct. For example, I want:

struct Registers {
  status: VolatileCell<usize>,
  control: VolatileCell<usize>,
  ...
}

and some global (private) variable:

static GPIO: &'static Registers;

It's currently possible to get this using an extern block and just declaring the variable in C or a linker script etc:

extern {
    // Works flawlessly
    static GPIO: &'static Registers;
}

Is there a way to do this purely in Rust?

I can get a raw pointer:

static GPIO: *const Registers = 0xdeadbeef as *const Registers`

but then I have to use unsafe in every reference of it, instead of just once when I declare it. Instead, it would be useful to be able to do:

static GPIO: &'static Registers = unsafe { &*(0xdeadbeef as *const Registers) };

or

static GPIO: &'static Registers = unsafe { mem::transmute(0xdeadbeef) };

This would allow you to localize unsafe clode blocks (making them easier to audit) and even extract them to a separate crate and compile everything else with unsafe not allowed. (In fact, this is basically the equivalent of using extern and linking with C).

Maybe this will be resolved if/when mem::transmute (and other compiler intrinsics) are declared const.

It probably makes sense to relax the "raw pointers cannot be dereferenced" rule a bit (E0396, the error you get for the first version). Of course, we'd have to make sure reading from memory is diagnosed correctly, but that's straightforward.

transmute will likely never be declared const, for the same reason we don't allow casting raw pointers to integers in constants. (Explanation.)

2 Likes

Yeah, I can see why just de-referencing is problematic, and i can see why this rule would then taint &* -- even though it's not actually dereferencing at compile-time, semantically it is. Thanks for the explanation.

Also makes sense.

As you mention it would be nice to have either relax the derferencing rule for operations that are actually casts, or perhaps some special syntax for (unsafely) casting from a raw pointer or usize to a &'static in static variables.

P.S.
I appreciate the links, very helpful for understand the motivations

From an API surface area perspective, what about const-endorsing as_ref? Although implementing that would be a different matter.

Seems reasonable. My concerns with that API are:

  • It presumes that 0x0 is a null-pointer, but in practice, e.g. on embedded ARM, it is often a meaningful pointer (on Cortex-Ms it often points to the first entry of the vector table).

  • It leaves for runtime something that is already attested to at compile time -- checking pointer validity -- so would be more cumbersome to use in most of the code than the extern equivalent.