当自我被借入封闭内部时,使用静态生命周期触发“封闭可能会超过功能”

时间:2018-02-07 06:58:17

标签: rust borrow-checker

我的程序使用变量的内存地址作为唯一标识符。我知道这非常难看,但它是获取唯一标识符的一种非常轻量级的方法。 此模式仅在我将这些变量设为静态时才有效,因此它们的唯一ID(即地址)永远“”,这意味着我有几个函数需要'static生命周期的引用。 / p>

我正在使用cortex-m crate,它提供了一种方法,可以将处理器置于允许函数在无中断临界区运行的状态。这是通过一个函数实现的,该函数将调用包装到需要在关键部分执行并具有适当的汇编调用的函数。

在这个人为的例子中,包装函数被称为run_in_special_state。我需要在特殊状态下执行foo方法。但是,它需要'static Contrived。这是一个说明错误的例子:

fn foo(_: &'static Contrived) {}

fn run_in_special_state<F, R>(f: F) -> R
where
    F: FnOnce() -> R,
{
    // Some stuff happens before the function
    let r = f();
    // Some stuff happens after the function
    r
}

struct Contrived {
    value: u32,
}

impl Contrived {
    fn func(&'static mut self) {
        run_in_special_state(|| foo(self));

        self.value = 6;
    }
}

static mut INSTANCE: Contrived = Contrived { value: 4 };

fn main() {
    unsafe { INSTANCE.func() };
}

这是你在操场上跑步时会得到的:

error[E0373]: closure may outlive the current function, but it borrows `self`, which is owned by the current function
  --> src/main.rs:19:30
   |
19 |         run_in_special_state(|| foo(self));
   |                              ^^     ---- `self` is borrowed here
   |                              |
   |                              may outlive borrowed value `self`
help: to force the closure to take ownership of `self` (and any other referenced variables), use the `move` keyword
   |
19 |         run_in_special_state(move || foo(self));
   |                              ^^^^^^^

我知道在FnOnce退出之前会调用run_in_special_state。我相信这也意味着闭包不会比当前函数(func?)更长,因为它(闭包)将在当前函数(func)退出之前执行并被丢弃。如何与借阅检查员沟通?还有其他事情发生在这里吗?我注意到,如果我将'static要求放在foo上,那么错误就会消失。

我无法执行建议的修复,因为我需要在调用self后使用run_in_special_state

1 个答案:

答案 0 :(得分:1)

这些功能的签名:

  • fn foo(_: &'static Contrived)

  • fn func (&'static mut self)

需要在整个程序期间借用其值的引用,而您需要的引用只需要足够长的时间借用它们的值。

删除'static,程序将编译:

fn foo(_: &Contrived) {}

fn run_in_special_state<F, R>(f: F) -> R
where
    F: FnOnce() -> R,
{
    // Some stuff happens before the function
    let r = f();
    // Some stuff happens after the function
    r
}

struct Contrived {
    value: u32,
}

impl Contrived {
    fn func(&mut self) {
        run_in_special_state(|| foo(self));

        self.value = 6;
    }
}

static mut INSTANCE: Contrived = Contrived { value: 4 };

fn main() {
    unsafe { INSTANCE.func() };
}

Playground

&'static T不仅仅是变量的地址,它还带有额外的语义。如果您想将它用作唯一标识符,那么您最好创建一个类型,它只保留地址的唯一性并且不会借用该值:

mod key {
    use super::Contrived;

    #[derive(Debug, Hash)]
    pub struct ContrivedId(usize);

    impl ContrivedId {
        pub fn new(r: &'static Contrived) -> Self {
            ContrivedId(r as *const _ as usize)
        }
    }
}

use key::ContrivedId;

fn foo(_: ContrivedId) {}

fn run_in_special_state<F, R>(f: F) -> R
where
    F: FnOnce() -> R,
{
    // Some stuff happens before the function
    let r = f();
    // Some stuff happens after the function
    r
}

pub struct Contrived {
    value: u32,
}

impl Contrived {
    fn func(&mut self, id: ContrivedId) {
        run_in_special_state(|| foo(id));

        self.value = 6;
    }
}

static mut INSTANCE: Contrived = Contrived { value: 4 };

fn main() {
    unsafe {
        let id = ContrivedId::new(&INSTANCE);
        INSTANCE.func(id)
    };
}

Playground