Background: I'm trying to avoid the use of Mutex
/RefCell
/Option
dance in an interrupt handler for an embedded system. I do not want to use the heap (and I don't think it should be necessary -- but feel free to show me wrong). I can't use std
. I've looked at cortex-m-rtfm
and it's neat, but pretty invasive. And anyway, this is a bit of a learning exercise. If it works out, I would prefer to use closures to handle interrupts since it feels closer to bare Rust. I am a total Rust newbie -- I've been working with it for about a week. I have tried a lot of different variants of this as I've read through documentation, re-read the Rust book, blog posts, etc.,. I cannot figure what I'm doing wrong here.
Here's the sample code. Questions to follow:
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
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()`
Explanation: Hopefully, my intentions are obvious: I'll set up the closure for HANDLER
in main
, before going into a busy-loop that never exits. The closure will mutably borrow the stuff the interrupt handler needs for its operation, preventing its use in other contexts. Since main
never exits, stack-allocated variables within it are effectively 'static
, so there shouldn't be a problem with referencing them at any point after the closure is set. The interrupt handler itself (not shown) will simply call the closure to do its work. To work around the storage of a closure (which is not Sized
) in a static, I need to store a reference to the closure. UnsafeCell
isn't necessarily required, but since I'm using FnMut()
its referents need to be mutable, which runs into statics require immutable values
when trying to set up default_handler
during the creation of a static mut HANDLER
.
Questions:
As posted, this code doesn't compile. For some reason, the assignment
let h: *mut FnMut() = self.h.get()
tells me that itexpected an Fn<()> closure, found (dyn std::ops::FnMut() + 'a)
. Well, I know why it found that type. But why is it expectingFn<()>
?In the
call_handler
test, why is this compiling at all? Thefoo
closuremove
s its captured variablea
. How is it possible to mutate it after the closure definition? When I've tried this code with a type that doesn't implementCopy
, it fails as expected, but I'm frankly surprised that trait matters. Isn't the point thatfoo
ownsa
now?
I realize there are potential issues with changing HANDLER.h
at arbitrary points in the code, but I will worry about solving those later, after there's a viable proof-of-concept.