How to deal with lifetime when need to expose through FFI

I am using specs that require some lifetime types to keep reference of the dispatcher. (specs::Dispatcher - Rust)

I have some context struct that hold things together.

pub struct Context<'a, 'b> {
    pub world: World,
    pub dispatcher: Dispatcher<'a, 'b>,

I am trying to use it through FFI by returning a handler pointer to the host language:

pub extern "C" fn init() -> *mut Context {
    let mut context = Context::new(value);

The the host language send data providing the handler

fn from_ptr(ptr: *mut Context) -> &'c mut Context {
    unsafe {
        &mut *ptr

pub extern fn update(ctx_ptr: *mut Context, delta: u32) -> u32 {
    let ctx = from_ptr(ctx_ptr);

From what I understand, there is no black magic to represent the lifecycle when casting it from raw pointers.

Any suggestion how to deal with it?

For now what I found:

A) Pray for success of encapsulation of private lifetimes (Pre-RFC: Encapsulating private lifetimes - language design - Rust Internals).

Too much hope?

B) Get rid of Dispatcher reference that require the lifecycles by dynamically creating each tick.

It will works, but this means whatever I use from now one can not have lifecycles.

Too much optimism?

C) Keep a unsafe global variable with Context and just share some KEY through FFI.

I am not complete even sure if it works.

Too much lack of knowledge?

The best thing then is to use an opaque type pattern:

struct FfiContext {
    _priv: [u8; 0],
    // Optional; to get the `Send` & `Sync` bounds right.
    _send_sync: ::core::marker::PhantomData<Context<'static, 'static>>,

#[no_mangle] pub extern "C"
fn init ()
  -> *mut FfiContext

fn from_ptr<'_0, '_1, '_2> (ptr: *mut FfiContext)
  -> &'_0 mut Context<'_1, '_2>
    if ptr.is_null() {
        eprintln!("Fatal error, got NULL `Context` pointer");
    &mut *(ptr.cast())

#[no_mangle] pub unsafe extern "C"
fn update (p: *mut FfiContext)
  -> u32
    let ctx: &'_ mut Context<'_, '_> = from_ptr(p);

#[no_mangle] pub unsafe extern "C"
fn free_context (p: *mut FfiContext)
    drop::<Box<Context<'_, '_>>>(Box::from_raw(from_ptr(p)))

Amazing, it is working.

I am surprised that you can cast into a struct with lifetimes. Looks like I need go back to read some documentation.


This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.