背景:我试图避免在嵌入式系统的中断处理程序中使用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
。
问题:
如前所述,此代码无法编译。由于某种原因,
作业let h: *mut FnMut() = self.h.get()
告诉我,
expected an Fn<()> closure, found (dyn std::ops::FnMut() + 'a)
。好吧,我知道为什么找到这种类型。但是为什么期望Fn<()>
?
在call_handler
测试中,为什么要完全编译? foo
闭包move
为其捕获的变量a
。在闭包定义之后如何进行变异?当我尝试使用未实现Copy
的类型的代码时,它按预期失败,但是坦率地说,特质很重要,我对此感到惊讶。 foo
现在拥有a
的意义不是?
我意识到在代码中的任意点更改HANDLER.h
可能存在一些问题,但是在有可行的概念证明之后,我将担心稍后解决这些问题。
答案 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
内部,以方便在运行时进行替换(在主循环内部且仅在内部)。