处理全局变量中的生命周期

Dealing with lifetimes in global variables

我目前正在为嵌入式板编写板支持包,并希望通过 USB 设置串行输出。我的设计基于 hifive BSP.

执行此操作的过程分为三个步骤:

  1. 设置USB总线(UsbBusAllocator,指的是UsbPeripheral
  2. 初始化一个SerialPort实例,它指的是UsbBusAllocator
  3. 初始化一个UsbDevice实例,它指的是UsbBusAllocator

为了简化我的生活,我将 SerialPortUsbDevice 包装在 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 未声明。

我考虑过使用 MaybeUninitPhantomData,但两者仍需要将 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 muts 非常不安全,甚至通过同时允许多个可变引用来绕过一些借用检查器机制。如果你要在 Rust 的类型系统中正确编码它,你可能只想让它成为 static STDOUT: SyncWrapperForUnsafeCell<Option<T>>,然后为你的包装器提供一个安全的接口(可能涉及锁定),并附上注释解释你当前的环境为什么这样做安全的。但是,如果您认为 static mut 是合适的选择,我相信您的判断。