I have an embassy task that takes an embassy BufferedUart<'a, _>. Embassy tasks require all parameters to have a lifetime of 'static. Because the BufferedUart must have a static lifetime, and the two &mut [u8;_] buffers must have the same lifetime as the BufferedUart, I believe I need static &mut [u8;_] buffers.
I am not sure how to get this in safe rust. I will only use these references in the new() function of the BufferedUart, so I believe there should be a way to prove to the compiler that this is safe.
Promoting a lifetime to 'static is never safe, though it may sometimes be sound (I'm not certain on that). If you can afford it, you could Box::leak the buffers, which would give you & 'static mut.
...which will store them inside itself in some way (otherwise it wouldn't have a lifetime on it), and task will use them somehow at the unspecified moment later (otherwise it wouldn't require 'static), therefore possibly leading to use-after-free, if they are references to some local variables.
Actually, could you share the relevant code - or at least show where BufferedUart and 'static requirement come from, so that we can check the docs ourselves?
I guess I forgot to mention. The buffers would be static, so I don't thiink I would need to Box::leak the buffers, and there wouldn't be a use after free since it is never freed. This is for a no_std environment.
Note that (unless the docs promise otherwise), even if the implementation really doesn't use it later, you still can't rely on that. Since the public facing API says 'static, it's allowed to break any code that passes non-static data, at any time, without notice, in a patch. So I still wouldn't rely on that.
If I use static mut buf: [u8;10] = [0;10]; getting a mut reference to this variable errors out because it requires unsafe. I would like to avoid the unsafe block. use of mutable static is unsafe and requires an unsafe function or block
Yes, because it is. Even in single threaded environments, it can cause UB.
You could use RefCell/Mutex/RwLock, and leak the guard you get from locking it. But in that case, the buffers will be unusable from then on. Unless the type gives you a way to access the buffers.
Alternatively, you could use unsafe and UnsafeCell, and pinky promise never to touch the buffers before the BufferedUart is dead.
In general, it's impossible to give out &'static mut, while retaining access to the original data, because it's a contradiction.
It seems to me that this ought to be implementable with a simple no_std-compatible “mutex you use only once”, as follows. However, I’ve never heard of a library that offers this, so perhaps I’m missing a reason this is unsound or unwise. Or perhaps it just exists under an unobvious name (I don't write embedded-focused Rust).
mod once_mut {
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use std::cell::UnsafeCell;
pub struct OnceMut<T> {
available: AtomicBool,
value: UnsafeCell<T>,
}
impl<T> OnceMut<T> {
pub const fn new(value: T) -> Self {
Self {
available: AtomicBool::new(true),
value: UnsafeCell::new(value),
}
}
pub fn take(&'static self) -> &'static mut T {
let available = self.available.swap(false, Ordering::Relaxed);
if available {
// SAFETY: available can only be true once on one thread,
// so there will only be at most one &mut reference
unsafe { &mut *self.value.get() }
} else {
panic!("attempted to reuse OnceMut");
}
}
}
// SAFETY: we synchronize with an AtomicBool,
// and we only give out `&mut` so no `T: Sync` is needed.
unsafe impl<T: Send> Sync for OnceMut<T> {}
}
use once_mut::OnceMut;
static BUF: OnceMut<[u8; 10]> = OnceMut::new([0; 10]);
pub fn main() {
let buf: &'static mut [u8; 10] = BUF.take();
}