在静态函数中使用FnMut()闭包

问题描述 投票:2回答:1

背景:我试图避免在嵌入式系统的中断处理程序中使用Mutex / RefCell / Option dance。我不想使用堆(我不认为它应该是必要的 - 但随意给我看错)。我不能使用std。我看了cortex-m-rtfm,它很整洁,但很有侵略性。无论如何,这是一个学习练习。如果它成功了,我宁愿使用闭包来处理中断,因为它感觉更接近裸锈。我是一个完整的Rust新手 - 我已经和它一起工作了大约一个星期。我已经尝试了很多不同的变体,因为我已经阅读了文档,重读了Rust书,博客文章等等。我无法想象我在这里做错了什么。

这是示例代码。要遵循的问题:

use core::cell::UnsafeCell;

pub struct Handler<'a> {
    h: UnsafeCell<&'a dyn FnMut()>,
}

impl<'a> Handler<'a> {
    pub fn new<T: FnMut()>(closure: &'a dyn FnMut()) -> Self {
        Handler {
            h: UnsafeCell::new(closure),
        }
    }

    pub fn call(&self) {
        unsafe {
            // NOTE: type returned by `self.h.get()` is
            // `*mut &'a (dyn std::ops::FnMut() + 'a)`
            let h: *mut FnMut() = self.h.get();
            h();
        }
    }
}
unsafe impl<'a> Sync for Handler<'a> {}

fn default_handler() {}

static HANDLER: Handler = Handler {
    h: UnsafeCell::new(&default_handler),
};

#[test]
fn call_handler() {
    let mut a: u32 = 0;
    let foo = move || a += 1;
    let mut handler = Handler::new(&foo);
    handler.call();
    a += 2; // Shouldn't this cause compilation failure because `a`
            // was moved into the closure above?
    assert_eq!(a, 1);
}

错误

error[E0618]: expected function, found `*mut dyn std::ops::FnMut()`
  --> src/lib.rs:19:13
   |
18 |             let h: *mut FnMut() = self.h.get();
   |                 - `*mut dyn std::ops::FnMut()` defined here
19 |             h();
   |             ^--
   |             |
   |             call expression requires function

error[E0277]: expected a `std::ops::Fn<()>` closure, found `(dyn std::ops::FnMut() + 'a)`
  --> src/lib.rs:18:35
   |
18 |             let h: *mut FnMut() = self.h.get();
   |                                   ^^^^^^^^^^^^ expected an `Fn<()>` closure, found `(dyn std::ops::FnMut() + 'a)`
   |
   = help: the trait `std::ops::Fn<()>` is not implemented for `(dyn std::ops::FnMut() + 'a)`
   = note: wrap the `(dyn std::ops::FnMut() + 'a)` in a closure with no arguments: `|| { /* code */ }
   = note: required because of the requirements on the impl of `std::ops::FnMut<()>` for `&'a (dyn std::ops::FnMut() + 'a)`
   = note: required for the cast to the object type `dyn std::ops::FnMut()`

说明:希望,我的意图是显而易见的:我将在HANDLER中设置main的闭包,然后进入一个永不退出的繁忙循环。闭包将可变地借用中断处理程序操作所需的东西,防止在其他上下文中使用它。由于main永不退出,其中的堆栈分配变量实际上是'static,因此在设置闭包后的任何时候都不应该引用它们。中断处理程序本身(未显示)将简单地调用闭包来完成其工作。要在静态中解决闭包(不是Sized)的存储,我需要存储对闭包的引用。 UnsafeCell不一定是必需的,但因为我使用FnMut()它的指示物需要是可变的,当试图在statics require immutable values的创建期间设置default_handler时会遇到static mut HANDLER

问题:

  1. 如发布,此代码无法编译。由于某种原因,任务let h: *mut FnMut() = self.h.get()告诉我它 expected an Fn<()> closure, found (dyn std::ops::FnMut() + 'a)。好吧,我知道它为什么找到那种类型。但为什么期待Fn<()>
  2. call_handler测试中,为什么要编译呢? foo关闭moves其捕获的变量a。如何在闭包定义之后改变它?当我尝试使用不实现Copy的类型的代码时,它会按预期失败,但我很惊讶特性很重要。现在不是foo拥有a的重点吗?

我意识到在代码中的任意点改变HANDLER.h存在潜在的问题,但是我会担心在有可行的概念验证之后解决这些问题。

rust embedded
1个回答
1
投票

我找到了一种方法来做我想做的事。对于一般用途来说,它是非常不安全的,必须调查隐藏其缺乏安全性的适当机制,甚至可能无法进行调查。主要技巧是使用as强制转换将可变特征对象转换为动态对象,并使用core::mem::transmute将其生命周期更改为static。这是代码:

use core::cell::UnsafeCell;
use core::mem::transmute;

struct Handler {
    h: UnsafeCell<*const dyn FnMut()>,
}

impl Handler {
    unsafe fn replace(&self, f: &dyn FnMut()) {
        let f_static: &'static dyn FnMut() = transmute(f);
        *self.h.get() = f_static;
    }

    unsafe fn call(&self) {
        let f: &mut dyn FnMut() = &mut *(*self.h.get() as *mut dyn FnMut());
        f();
    }
}
unsafe impl Sync for Handler {}

fn default_handler() {}
static HANDLER: Handler = Handler {
    h: UnsafeCell::new(&default_handler),
};

fn main() {
    let mut x: u32 = 0;
    let closure = || x += 2;

    unsafe {
        HANDLER.replace(&closure);
        HANDLER.call();
    };
    println!("x: {}", x); // Prints 2
}

Handler.h包裹的封闭物位于UnsafeCell内,以便于在运行时(内部,仅在主循环内部)进行更换。

© www.soinside.com 2019 - 2024. All rights reserved.