Custom placed FnOnce: how to call properly?

Hi,

I'm toying with coroutines, related ASM and custom placement.
What I basically have at some point is some extern function which receives address of some service struct, manually placed somewhere in RAM. That extern function must invoke that struct as a () -> () function and then drop it properly, BUT without deallocating its memory, moving it somewhere etc.

First I thought about just placing &FnOnce() + Drop fat pointer near struct, and reference it. But it's not possible because FnOnce should own and move its context. That struct will also definitely contain FnOnce()+Send+'static, which must be also called properly.

The only idea I have is to read &FnOnce at provided address and then unsafely (*ref)().
Any other ideas?

Thanks

Maybe something like this?

use std::ptr;
use std::mem;
unsafe fn call_once_ptr<T: FnOnce()>(p: *mut ()) {
     ptr::read(p as *mut T)();
}
struct OnceCaller {
    ptr: *mut (),
    f: unsafe fn(*mut ())
}
fn get_once_caller<T: FnOnce()>(ptr: *mut T) -> OnceCaller {
    OnceCaller { ptr: ptr as *mut (), f: call_once_ptr::<T> }
}
unsafe fn do_call_once(o: OnceCaller) {
    (o.f)(o.ptr)
}
fn main() {
    let mut once = || println!("asdf");
    let c = get_once_caller(&mut once);
    unsafe { do_call_once(c); }
    mem::forget(once);
}

You can probably make it a bit nicer, but the basic idea is to manually build a "trait reference" of sorts.

2 Likes

Won't it be moved? Or Rust is smart enough to not move data unless necessary?

Oh. You're right, it'll move the data. There isn't any way in Rust currently to write ptr::read(p as *mut T)(); in a way that avoids the move. Of course, depending on the calling convention you're using, there might be a hard requirement that the data gets moved anyway.

If you really need to avoid copying, you shouldn't use FnOnce; just implement the relevant convention by hand.