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

时间:2019-04-23 20:32:26

标签: rust embedded

背景:我试图避免在嵌入式系统的中断处理程序中使用Mutex / RefCell / Option跳舞。我不想使用堆(我不认为这是必要的-但请随时向我展示错误)。我不能使用std。我看过cortex-m-rtfm,它很简洁,但是很容易侵入。无论如何,这是一个学习练习。如果可以解决,我宁愿使用闭包来处理中断,因为它感觉更接近裸露的Rust。我完全是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闭包move为其捕获的变量a。在闭包定义之后如何进行变异?当我尝试使用未实现Copy的类型的代码时,它按预期失败,但是坦率地说,特质很重要,我对此感到惊讶。 foo现在拥有a的意义不是?

我意识到在代码中的任意点更改HANDLER.h可能存在一些问题,但是在有可行的概念证明之后,我将担心稍后解决这些问题。

1 个答案:

答案 0 :(得分:0)

我找到了一种做自己想做的方法。它对于一般用途来说是绝对不安全的,因此必须研究隐藏其缺乏安全性的适当机制,甚至可能无法做到。主要技巧是使用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内部,以方便在运行时进行替换(在主循环内部且仅在内部)。