【问题标题】:Using static lifetimes triggers "closure may outlive function" when self is borrowed inside a closure当 self 在闭包内借用时,使用静态生命周期会触发“闭包可能比函数寿命更长”
【发布时间】:2018-07-17 09:12:50
【问题描述】:

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

我正在使用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)退出之前被执行并丢弃。我怎样才能将此传达给借阅检查员?这里还有其他事情吗?我注意到,如果我在 foo 上放弃 'static 要求,错误就会消失。

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

【问题讨论】:

    标签: rust borrow-checker


    【解决方案1】:

    这些函数的签名:

    • fn foo(_: &amp;'static Contrived)

    • fn func (&amp;'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

    &amp;'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

    【讨论】:

    • ContrivedId 还不如来个&amp;'static T
    猜你喜欢
    • 1970-01-01
    • 2020-07-15
    • 2019-07-02
    • 2022-01-14
    • 2019-11-13
    • 1970-01-01
    • 2020-01-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多