In an ffi (embedded) environment a trampoline is usually used when the C code expects a function pointer (and an optional, usually *void argument), but we want to have it call a rust closure instead.
The trampoline is an fn
, while a boxed closure is supplied as the argument. The fn code simply calls the closure.
However it seems that I can't convince the borrow checker that the following code is safe, and it seems to think that a reference given to the closure as parameter (not captured by the closure) remains borrowed after the call to the closure returns.
I'm not sure why this is the case, can someone help out on how to make this compile?
// Workaround for unstable feature 'trait_alias'
pub trait Callback<'a>: FnMut(&'a mut Context<'a>) {}
impl<'a, T> Callback<'a> for T where T: FnMut(&'a mut Context<'a>) {}
pub fn trampoline(cb_ptr: *mut c_void ) {
let s = "foo";
let mut ctx = Context { s };
unsafe {
let cb = cb_ptr as *mut Box<dyn Callback<'_>>;
(*cb)(&mut ctx);
}
let Context { s, .. } = ctx;
info!("s = {}", &s);
}
Error:
error[E0503]: cannot use `ctx.s` because it was mutably borrowed
--> temp.rs:91:19
|
88 | (*cb)(&mut ctx);
| -------- borrow of `ctx` occurs here
...
91 | let Context { s, .. } = ctx;
| ^
| |
| use of borrowed `ctx`
| borrow later used here
For more information about this error, try `rustc --explain E0503`.
The problem appears to be with &'a mut Context<'a>
. This is saying that the reference to the context needs to live as long as the reference within the context. However, you actually don't care about the lifetime of the reference to the context; it only needs to last for the duration of the call to the closure.
Changing &'a mut
to just &mut
appears to fix the issue:
// Workaround for unstable feature 'trait_alias'
pub trait Callback<'a>: FnMut(&mut Context<'a>) {}
impl<'a, T> Callback<'a> for T where T: FnMut(&mut Context<'a>) {}
pub fn trampoline(cb_ptr: *mut c_void ) {
let s = "foo";
let mut ctx = Context { s };
unsafe {
let cb = cb_ptr as *mut Box<dyn Callback<'_>>;
(*cb)(&mut ctx);
}
let Context { s, .. } = ctx;
info!("s = {}", &s);
}
playground
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With