Expected fn pointer, found closure

I have the following types and functions in my code:

type IrqList = FnvIndexMap<u8, MiniVec<fn(InterruptStackFrameValue)>, U256>;
//...
static ref IRQ_FUNCS: RwLock<IrqList> = RwLock::new({
//...
pub fn register_interrupt_handler(interrupt: u8, func: fn(InterruptStackFrameValue)) -> usize
//...

And I call the above fn like so:

        register_interrupt_handler(self.intline, |_| self.cqs.iter().enumerate().for_each(|(i, queue)| queue.read_new_entries(&mut self.resps[i])));

But when I compile it, I get:

error[E0308]: mismatched types
   --> libk\src\nvme\mod.rs:695:50
    |
695 | ...(self.intline, |_| self.cqs.iter().enumerate().for_each(|(i, queue)| queue.read_new_entries(&mut self.resps[i])));
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found closure
    |
    = note: expected fn pointer `fn(InterruptStackFrameValue)`
                  found closure `[closure@libk\src\nvme\mod.rs:695:50: 695:147]`

I understand that this might have lifetime issues, too, but I'm kinda confused. When I originally had this function prototype defined, it took no arguments and I could happily register interrupt handlers all day with it. But now, when I introduce the stack frame, it complains about it. I've tried Boxing it, which gives me the lifetime error. My question is: how do I register this handler in such a way that I can have it only affect this particular instance of it? I could do an interrupt handler for all of the devices that this driver is able to work with -- all the devices that I've found, I mean. But the problem is that each device may have a different interrupt number, and though I could change that interrupt vector, I don't really want to. I could register the same handler for every new interrupt vector I get, but that seems like a waste to me -- I'd have multiple interrupts doing the same thing. Is there any way to do this or should I just register a handler that scans all the devices and runs this code on each one?

I think that the problem is you're trying to pass information other than InterruptStackFrameValue (such as borrowed variables from the parent scope, &self for example).
The way I see it - fn is like a regular function in C, you can't do fancy closure stuff with it. Closures are an fn with another hidden magic argument that contains all the required borrows from the caller.

I would consider changing register_interrupt_handler's func argument to a &'static dyn Fn(InterruptStackFrameValue) / Box<dyn Fn(InterruptStackFrameValue)>

I tried that and got all sorts of lifetime problems. I can't for example store that without adding +'static+Send+Sync onto every signature of the function.

I would suggest using a type alias to prevent 'static+Send+Sync everywhere.

Example: (or playground)

Click here to expand
use indexmap::{IndexMap as FnvIndexMap, map::Entry}; // 1.6.0
use std::sync::RwLock;
use lazy_static::lazy_static; // 1.4.0
use std::vec::{Vec as MiniVec};

struct InterruptStackFrameValue;

pub type InterruptHandler = &'static (dyn Fn(InterruptStackFrameValue) + Send + Sync);

type IrqList = FnvIndexMap<u8, MiniVec<InterruptHandler>/*, U256*/>;

lazy_static! {
    static ref IRQ_FUNCS: RwLock<IrqList> = RwLock::new(FnvIndexMap::new());
}

pub fn register_interrupt_handler(interrupt: u8, func: InterruptHandler) -> usize {
    let mut lock = IRQ_FUNCS.write().unwrap();
    match lock.entry(interrupt) {
        Entry::Occupied(mut c) => {
            c.get_mut().push(func);
        },
        Entry::Vacant(v) => {
            v.insert(vec![func]);
        }
    }
    
    0
}

fn main() {
    register_interrupt_handler(0x80, &|isfv| {
        // do something!
    });
}

This does not work. It raises:

error[E0596]: cannot borrow `controller` as mutable, as it is a captured variable in a `Fn` closure
   --> libk\src\nvme\mod.rs:749:47
    |
749 | register_interrupt_handler(dev.int_line, &|s| controller.handle_interrupt(s));
    |                                               ^^^^^^^^^^ cannot borrow as mutable

error[E0716]: temporary value dropped while borrowed
   --> libk\src\nvme\mod.rs:749:43
    |
749 | register_interrupt_handler(dev.int_line, &|s| controller.handle_interrupt(s));
    |                                          -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
    |                                          ||
    |                                          |creates a temporary which is freed while still in use
    |                                          cast requires that borrow lasts for `'static`

error[E0597]: `controller` does not live long enough
   --> libk\src\nvme\mod.rs:749:47
    |
749 | register_interrupt_handler(dev.int_line, &|s| controller.handle_interrupt(s));
    |                                          -----^^^^^^^^^^--------------------
    |                                          ||   |
    |                                          ||   borrowed value does not live long enough
    |                                          |value captured here
    |                                          cast requires that `controller` is borrowed for `'static`
750 | controllers.push(controller);
751 | }
    | - `controller` dropped here while still borrowed

error[E0505]: cannot move out of `controller` because it is borrowed
   --> libk\src\nvme\mod.rs:750:18
    |
749 | register_interrupt_handler(dev.int_line, &|s| controller.handle_interrupt(s));
    |                                          -----------------------------------
    |                                          ||   |
    |                                          ||   borrow occurs due to use in closure
    |                                          |borrow of `controller` occurs here
    |                                          cast requires that `controller` is borrowed for `'static`
750 | controllers.push(controller);
    |                  ^^^^^^^^^^ move out of `controller` occurs here

Additionally, I'm getting this weird error:

error[E0500]: closure requires unique access to `self` but it is already borrowed
   --> libk\src\nvme\mod.rs:740:42
    |
740 | self.cqs.iter_mut().enumerate().for_each(|(i, queue)| queue.read_new_entries(&mut self.resps[i]));
    | --------                        -------- ^^^^^^^^^^^^                             ---- second borrow occurs due to use of `self` in closure
    | |                               |        |
    | |                               |        closure construction occurs here
    | borrow occurs here              first borrow later used by call

Its in this code:

pub fn handle_interrupt(&mut self, _: InterruptStackFrameValue) {
self.cqs.iter_mut().enumerate().for_each(|(i, queue)| queue.read_new_entries(&mut self.resps[i]));
}

The explanation for this error says I can fix it by cloning self, but I imagine that really wouldn't go over very well. Thoughts?
Edit: Yeah, most of these errors are trivial Rust errors but I really do want some way of solving this in. I have one last idea -- using controller IDs or something like that to figure out what controller is what -- and though that will eliminate those four errors involving controller, I'm not sure about the last one.

Right, the Fn only allows immutable borrows, using FnMut should solve that.
Additionally, when registering a new interrupt hander, Rust needs to know that values from the current scope will be available when the closure is actually called - that's why it suggests to clone or to move.
I would suggest to wrap self in an Arc and clone the Arc if possible.

If it's possible for you to share the code, it would make it easier for members to help out...

Of course! The code is over here - https://github.com/ethindp/kernel

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.