Ffi that only seems to work with debug builds

I have something similar to the following -

#[repr(C)]
pub struct COokle {
    onk: libc::size_t,
    ponk: libc::size_t,
}

#[no_mangle]
pub extern "C" fn ookle() -> *const libc::c_void {
    let mut res = COokle { onk: 3,
                           ponk: 8 };
    
    let ptr: *mut libc::c_void = &mut res as *mut _ as *mut libc::c_void;

    mem::forget(res);
    ptr
}

and then in Haskell :

data COokle = COokle { onk :: Int64
                     , ponk :: Int64 }
  deriving (Show, Generic)

instance CStorable COokle
instance Storable COokle where
  sizeOf = cSizeOf
  alignment = cAlignment
  poke = cPoke
  peek = cPeek

foreign import ccall unsafe "ookle" rs_ookle :: IO (Ptr COokle)

getOokle :: IO (Int, Int)
getOokle = do
  cook <- rs_ookle
  cook' <- peek cook
  return (fromIntegral $ onk cook',
          fromIntegral $ ponk cook')

If I build a debug release of my rust library, everything works perfectly fine from Haskell.

*Lib> getOokle                                                                                       
(3,8)    

But if I link to a release build it doesn't .

*Lib> getOokle
(140063742853056,0)

I am clearly doing something wrong, but I'm not really sure what. Also I don't quite understand what the debug and release builds do differently that would produce the different results.

Any ideas?

Thanks

You have to box your resource, otherwise you are returning a pointer to your Rust functions current stack frame, which is UB

#[no_mangle]
pub extern "C" fn ookle() -> *const libc::c_void {
    let res = Box::new(COokle { onk: 3,
                           ponk: 8 });
    
    Box::into_raw(res) as *const libc::c_void
}
1 Like

To free the resource, you could write this rust function:

#[no_mangle]
pub extern "C" fn free_ookle(p: *const libc::c_void) {
    let b = unsafe { Box::from_raw(p as *mut COokle) };
    mem::drop(b); // THis is optional (Box::drop) is called automatically when resource goes out of scope
}

Also, you probably should not cast everything into void pointers. I don`t have a clue, how haskell handles FFI, but I am pretty sure it can interact with properly typed arguments in C functions, so you should be fine using * mut COokle as your argument type.

Maybe also consider not using pointers / heap at all. It might just work returning the COokle directly (if haskell can manage, again no idea if it can). COokle is a pretty simple POD type, and you don`t really gain anything by boxing it, except when haskell can only work with FFI pointer like objects.

Yes. Perfect, that works. Many thanks.

Yes, it seems to work fine returning *const COokle so the void cast is uneccessary.

In reality I am returning more complicated structures, structs containing vectors of structs containing strings and the like, but that all works fine as long as I box the return.

Now on to look into freeing it all successfully. Many thanks for the pointer!