Passing long-lived closure with references to C FFI


#1

I have a C library that wants to be able to call a callback multiple times. The lifetime of the callback is limited to lifetime of C library’s “handle”, so theoretically any closure that outlives the handle should be safe to use as the callback.

I would like this to be possible:

let mut counter = 0;
let handle = Handle::new(); 
handle.set_callback(|| counter+=1);
handle.do_stuff(); // Will cause C to call the callback multiple times
// counter is > 0 now

I don’t know what’s the good way to do this on Rust side. If I do:

impl Handle {
    pub fn set_callback(cb: &mut FnMut()) {…}
}

then it can’t be called with handle.set_callback(|| …), but requires &mut || …. It’s suspicious, because I’ve never seen this type used anywhere else in Rust. Also, storing it in PhantomData to ensure it outlives the handle causes explosion of lifetime annotations all over my code.

If I do:

impl Handle {
    pub fn set_callback<F>(cb: F) … {…}
}

then AFAIK I have to store the callback in Box::new(cb), but that requires F: 'static, and prevents the callback from referencing outer variables, which makes it much less convenient to use.

And in all cases casting/transmuting the closure pointer is tricky, because it’s a fat pointer. What is the right way to do that?

Playpen: https://is.gd/itLbxe


#2

I think this would be the correct signature:

impl Handle {
    pub fn set_callback<'a, F: Fn() + 'a>(&'a self, cb: F) ... {...}
}

Note that you mustn’t move the closure while you have passed a pointer to it to C.


#3

Also, here’s how to correctly call a closure from a callback with user data: https://github.com/jethrogb/rust-mbedtls/blob/master/mbedtls/src/wrapper_macros.rs#L31-L39 This uses a trait but you don’t have to.