I know sharing variables between threads without a Mutex is considered bad practice, but I still believe it could have preformance benefits.
So my question is what's the difference between the two following lines of code, and which should I use?
static mut ARRAY_0: [u8; 1 << 30] = [0; 1 << 30];
static mut ARRAY_1: SyncUnsafeCell<[u8; 1 << 30]> = SyncUnsafeCell::new([0; 1 << 30]);
I tried googling, but I only found recommendations of using Mutexes, and examples with SyncUnsafeCell. I would like to know how does each of the implementations differ, and what are the alternatives.
SyncUnsafeCell
is so that you don't need to use static mut
and can instead use a normal static:
#![feature(sync_unsafe_cell)]
use std::cell::SyncUnsafeCell;
static ARRAY_1: SyncUnsafeCell<[u8; 1 << 30]> = SyncUnsafeCell::new([0; 1 << 30]);
fn foo() {
unsafe {
(*ARRAY_1.get())[0] = 123;
}
}
Specifically, there are two ways to mutate a static
:
static mut
.UnsafeCell
. This is e.g. how Mutex
works: you don't mark the static
as mut
, so how do you mutate it? It's because Mutex
wraps the data in UnsafeCell
.SyncUnsafeCell
contains UnsafeCell
too, so it is using the second way.
The reason to use it instead of static mut
is that static mut
is very hard to use correctly, and soundness bugs were found even in fundamental libraries using it such as lazy_static
. For this reason it is better avoided, and is even being softly deprecated (in edition 2024, taking a reference to static mut
will lint, only addr_of[_mut]!()
will be allowed). It was considered for removal, but that was deemed too pervasive.