Open jamesmunns opened 11 months ago
Coming here from the Matrix chat.
For me there are three main uses of static mut
s, although mostly they have to do with uninit memory and hardware restricted choices, which do not map well to Rust's model.
OnceCell
or similar abstractions. As I'm mainly developing on the driver side, I mainly use statics to map to these regions and offer to the user only a safe view into the hardware buffer. An example of this would be DMA descriptor regions, SDMMC hardware buffers (usually 512 bytes) and similar. This also maps well to external memories (e.g. STM32 external SDRAM memory access region).unwrap
ing and match
ing everywhere) I simply create a const fn
method I call empty
which fills the structs with 0s and then link those statics in the .bss or .uninit section (depends on if the initial state needs to be 0s). This skips initialization and zeroing of memory that does not need to be initialized, improving performance somewhat.Hope it helps
I've use them once in a while if I want to prove something to me. For example, I'm inspecting some generated assembly and I need a write to not be optimized away. A static mut is a quick way to get that in place of a let mut.
Another thing I can imagine, but haven't done myself so far, is having to have a global variable for compat with C. Say a C lib has a global extern variable, how would you create that un Rust without static mut?
@spcan that definitely helps! Grounded already has some tooling for getting a pointer to a static uninit item or array of items, that I believe to be more sound of an abstraction to build on top of (check out GroundedCell
and GroundedArrayCell
). The API is just a single .get()
that returns an *mut T
, which is often what you need for DMA, and allows you to "bring your own safety" on top of it if you want to create a reference from it.
I definitely want to add items (or examples) that show how to use it with specifically defined linker sections, in case it needs to be mapped at a specific location.
Additionally:
This model does not map well to Rust because I feel there are no good ways of telling it, 'Yes, this is undefined behaviour, but I know the state this section of memory will be when I use it'
Just nitpicking, the compiler is allowed to assume that undefined behavior never exists, and this can cause it to optimize your program in unexpected ways, including removing whole chunks of it, even if you use unsafe
!
Similarly to @diondokter, in theory a static mut should be no more resistant than any other mut variable to being optimized out. Though if it's a single threaded example and you are very careful to not create aliasing references, it shouldn't be immediately UB (just very eas to accidentally cross the line).
Another thing I can imagine, but haven't done myself so far, is having to have a global variable for compat with C. Say a C lib has a global extern variable, how would you create that un Rust without static mut?
From my answer in chat:
The answer is probably It Depends (based on what kinds of guarantees the C program makes). For example, if the C program ever leaves the global in an inconsistent state (say: a bool with "2" in it), then you definitely shouldn't expose it as just a static mut.
however, an UnsafeCell<MaybeUninit
> or just UnsafeCell can also be used as a non-mut static. IMO: basically anywhere you use static mut you should basically be using UnsafeCell
or UnsafeCell<MaybeUninit >.
Also, thank you both for sharing! I do want to share why I want to provide other options, and both of your examples help a ton on what kinds of structures, docs, and examples I should provide!
I'd love to get rid of this one: https://github.com/ferrous-systems/rust-exercises/blob/7e6d9e6029ad9a31b0c75d233dda6921cd8421f0/nrf52-code/boards/dongle/src/lib.rs#L294
It's a static mut
in a function that can only be called once (it uses Peripherals::take()
to ensure this). It has to be static because there are stack-allocated structures elsewhere that want to hold a &Clocks
and the only reasonable way to do this was to make it a &'static Clocks
.
@thejpster your example is nearly exactly the use case of StaticCell
, widely used in embassy.
I've avoided duplicating it because static cell is already well implemented and widely used.
Neat, I'll just use that then.
I use static mut
s for basically all the context in my current project. Quickly summarizing the SDK, you implement callbacks / message handlers that are called from the kernel. Chip is called DA14531. Code is over here.
Looks something like this:
ps. Perhaps there is a better way (with less statics) to integrate with the SDK I'm using.
If you have an example of why you use/need
static mut
, particularly in an embedded context, I'm interested in hearing why! This will help me provide safer alternatives ingrounded
in the future.If you can share a link to code, or a snippet inline or on the Rust Playground, that would help out a ton.