Reputation: 2093
I am currently writing a board support package for an embedded board and would like to set up a serial output over USB. I am basing my design on the hifive
BSP.
The process to do this is in three steps:
UsbBusAllocator
, which refers to a UsbPeripheral
)SerialPort
instance, which refers to UsbBusAllocator
UsbDevice
instance, which refers to UsbBusAllocator
To make my life simpler, I wrapped the SerialPort
and UsbDevice
in a SerialWrapper
struct:
pub struct SerialWrapper<'a> {
port: SerialPort<'a, Usbd<UsbPeripheral<'a>>>,
dev: UsbDevice<'a, Usbd<UsbPeripheral<'a>>>,
}
impl<'a> SerialWrapper<'a> {
pub fn new(bus: &'a UsbPort<'a>) -> Self {
// create the wrapper ...
}
}
I would like a way make the structure created by the SerialWrapper::new
global.
I tried to use:
static mut STDOUT: Option<SerialWrapper<'a>> = None;
but I can't use this as lifetime 'a
is not declared.
I thought about using MaybeUninit
or PhantomData
but both will still need to have SerialWrapper<'a>
as type parameter and I will get the same issue.
My goal is to be able to use code similar to this:
struct A;
struct B<'a> {
s: &'a A,
}
static mut STDOUT: Option<B> = None;
fn init_stdout() {
let a = A {};
unsafe {
STDOUT = Some(B {s: &a});
}
}
// access_stdout is the important function
// all the rest can be changed without issue
fn access_stdout() -> Result<(), ()> {
unsafe {
if let Some(_stdout) = STDOUT {
// do stuff is system ready
Ok(())
} else {
// do stuff is system not ready
Err(())
}
}
}
fn main() {
init_stdout();
let _ = access_stdout();
}
Do you have any suggestions on how to proceed?
I don't mind having a solution requiring unsafe code, as long as I can have safe functions to access my serial port.
Upvotes: 1
Views: 795
Reputation: 1951
Short answer: whenever you have a lifetime in the type of a static variable, that lifetime needs to be 'static
.
Rust does not allow for dangling references, so if you having anything living for shorter than the static lifetime in a static variable, there's the possibility for dangling. I think your code will need a fair amount of refactoring to satisfy this requirement. I would recommend figuring out a way to store the data you need without references since that will make your life much easier. If it is absolutely imperative that you store references you'll need to figure out a way to leak the data to extend its lifetime to 'static
.
I am not terribly familiar with embedded development, and I know that static mut
has some use-cases there, however usage of that language feature is pretty universally frowned upon. static mut
s are wildly unsafe and even bypass some borrow checker mechanisms by allowing multiple mutable references at the same time. If you were to encode this properly in Rust's type system you'd probably want to make it just static STDOUT: SyncWrapperForUnsafeCell<Option<T>>
, and then provide a safe interface (that might involve locking) for your wrapper with comments explaining why your current environment makes it safe. However if you think that a static mut
is the appropriate option there I trust your judgement.
Upvotes: 1