Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FnMut seems to capture it's call parameters?

Tags:

rust

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`.

like image 398
Ákos Vandra-Meyer Avatar asked Oct 17 '25 02:10

Ákos Vandra-Meyer


1 Answers

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

like image 147
PitaJ Avatar answered Oct 20 '25 00:10

PitaJ