For example, is there any problems with doing this?
fn main() {
static mut BUF: [u8; 0x400] = [0; 0x400];
let buf = &mut unsafe { BUF };
}
and is this code the same as just using an array directly? From my understanding local variables get put on the stack but do the static variables do too?
I’m essentially trying to find the most performant way to get a simple read/write buffer.
I’m essentially trying to find the most performant way to get a simple read/write buffer.
Stack is hot so it’s probably better to put things there than to have static array which is out of memory cache and whose address is out of TLB.
To answer your question, yes, this is undefined behaviour if the function is called from multiple threads. It’s also undefined behaviour if, by accident, you take second reference to the array.
It’s unlikely that you really need to do anything fancy. I/O is usually orders of magnitude slower than dealing with memory buffers. Unless you profile your code and find the bottleneck, I’d advice against static mutable buffer.
PS. On related note, a shameless plug: Rust’s worst feature.
Thanks! That’s exactly the answer I was looking for.
The premature optimisation quote at the end of your blog post is very relevant to me. I try do find the most efficient way right off the bat so very frequently the first questions I have in a project are like this one. Which in turn lead me to understand the underlying basics, which make me want to implement those basics myself, which sends me down a spiral to wanting to write my own kernel. All the while the project I started with gets forgotten, until I pick it up a month later and the whole thing starts again.
Maybe I should just try and learn C… or zig. And try and hold myself to the higher quality standard they demand. Especially since it feels like I’m doing that already.
What made you reach out to a
static mutin the first place?My logic was simply: I need a buffer that is only initialised once no matter how many times the function is called. statics are initialised at program start so they seemed like a good fit. and since I wasn’t planning for the function to me called multiple times simultaneously it seemed like the UB didn’t matter. (which I think was correct)
UB didn’t matter.
There is no such a thing.
If you really must pretend this matters performance wise, look up
MaybeUninit. It still requiresunsafe{}, but it’s a lot less trouble.
Another commenter already explained why this is unsound, so I’ll skip that, though
static mutis almost universally unsound.Note, of course, that
main()won’t be called more than once, so if you can, I would honestly just make this a stack variable containing aBox<[u8; 0x400]>instead. Alternatively, aBox<[u8]>can make it simpler to pass around, and aVec<u8>that is pre-allocated withVec::with_capacitylets you track the current length as well with the buffer (if it’s going to have a variable length of actually useful data).If you want to make it a static for some reason, I’d recommend making it just
staticandthread_local, then wrapping it in some kind of cell. Making it thread local will mean you don’t need to lock to access it safely.The problem with static mut is that it allows you to create multiple mutable references. And also mix mutable and immutable references. Additionally, it is accessible by any thread. So, as long as you don’t do any of that, it should be safe. Maybe I’m missing something.
If this is the entire program, it’s not unsafe. But if it is just a fraction of the program, it may be unsafe. For example if 2 threads call the function at the same time. Since you would have 2 mutable references to BUF. Well, not actually unsafe since you don’t use the mutable reference, only create it.
As to the other question, static variables are not in the stack. They have their own region of memory. If they were in the stack, they couldn’t be accessed across threads, since each thread has its own stack.
EDIT: for completeness sake. For your last question. Yes, using a static buffer is probably more performant, since it doesn’t need to be set to 0 each time it’s called. However, that’s not what statics are for. If what you want is just to avoid that setting to 0, there are ways to get initialized arrays. For example MybeUninit. Which would be way better.


