处理全局变量中的生命周期
Dealing with lifetimes in global variables
我目前正在为嵌入式板编写板支持包,并希望通过 USB 设置串行输出。我的设计基于 hifive
BSP.
执行此操作的过程分为三个步骤:
- 设置USB总线(
UsbBusAllocator
,指的是UsbPeripheral
)
- 初始化一个
SerialPort
实例,它指的是UsbBusAllocator
- 初始化一个
UsbDevice
实例,它指的是UsbBusAllocator
为了简化我的生活,我将 SerialPort
和 UsbDevice
包装在 SerialWrapper
结构中:
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 ...
}
}
我想要一种方法使 SerialWrapper::new
创建的结构全局化。
我尝试使用:
static mut STDOUT: Option<SerialWrapper<'a>> = None;
但我不能使用它,因为生命周期 'a
未声明。
我考虑过使用 MaybeUninit
或 PhantomData
,但两者仍需要将 SerialWrapper<'a>
作为类型参数,我会遇到同样的问题。
我的目标是能够使用类似于这样的代码:
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();
}
你对如何进行有什么建议吗?
我不介意有一个需要不安全代码的解决方案,只要我可以有安全的功能来访问我的串行端口。
简短回答:只要你有静态变量类型的生命周期,生命周期就需要 'static
。
Rust 不允许悬挂引用,因此如果您有任何比静态变量中的静态生命周期短的东西,则有可能出现悬挂引用。我认为您的代码需要进行大量重构才能满足此要求。我建议找出一种无需引用即可存储所需数据的方法,因为这将使您的生活更加轻松。如果存储引用绝对必要,您将需要想办法泄漏数据以将其生命周期延长至'static
。
我对嵌入式开发不是很熟悉,我知道 static mut
有一些 use-cases,但是普遍不赞成使用该语言功能。 static mut
s 非常不安全,甚至通过同时允许多个可变引用来绕过一些借用检查器机制。如果你要在 Rust 的类型系统中正确编码它,你可能只想让它成为 static STDOUT: SyncWrapperForUnsafeCell<Option<T>>
,然后为你的包装器提供一个安全的接口(可能涉及锁定),并附上注释解释你当前的环境为什么这样做安全的。但是,如果您认为 static mut
是合适的选择,我相信您的判断。
我目前正在为嵌入式板编写板支持包,并希望通过 USB 设置串行输出。我的设计基于 hifive
BSP.
执行此操作的过程分为三个步骤:
- 设置USB总线(
UsbBusAllocator
,指的是UsbPeripheral
) - 初始化一个
SerialPort
实例,它指的是UsbBusAllocator
- 初始化一个
UsbDevice
实例,它指的是UsbBusAllocator
为了简化我的生活,我将 SerialPort
和 UsbDevice
包装在 SerialWrapper
结构中:
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 ...
}
}
我想要一种方法使 SerialWrapper::new
创建的结构全局化。
我尝试使用:
static mut STDOUT: Option<SerialWrapper<'a>> = None;
但我不能使用它,因为生命周期 'a
未声明。
我考虑过使用 MaybeUninit
或 PhantomData
,但两者仍需要将 SerialWrapper<'a>
作为类型参数,我会遇到同样的问题。
我的目标是能够使用类似于这样的代码:
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();
}
你对如何进行有什么建议吗?
我不介意有一个需要不安全代码的解决方案,只要我可以有安全的功能来访问我的串行端口。
简短回答:只要你有静态变量类型的生命周期,生命周期就需要 'static
。
Rust 不允许悬挂引用,因此如果您有任何比静态变量中的静态生命周期短的东西,则有可能出现悬挂引用。我认为您的代码需要进行大量重构才能满足此要求。我建议找出一种无需引用即可存储所需数据的方法,因为这将使您的生活更加轻松。如果存储引用绝对必要,您将需要想办法泄漏数据以将其生命周期延长至'static
。
我对嵌入式开发不是很熟悉,我知道 static mut
有一些 use-cases,但是普遍不赞成使用该语言功能。 static mut
s 非常不安全,甚至通过同时允许多个可变引用来绕过一些借用检查器机制。如果你要在 Rust 的类型系统中正确编码它,你可能只想让它成为 static STDOUT: SyncWrapperForUnsafeCell<Option<T>>
,然后为你的包装器提供一个安全的接口(可能涉及锁定),并附上注释解释你当前的环境为什么这样做安全的。但是,如果您认为 static mut
是合适的选择,我相信您的判断。