Return C struct (pointer) from ffi

I am struggling using ffi to call an external C function and return a struct allocated in the function. I am using a u64 handle from a previously call and passing it in to the function. The prototype (rust) looks as follows :

extern "C" {
    fn Get_MetaData(handle : u64, name : *const c_char) -> * const MetaDataList;
}

Rust Structures

#[repr(C)]
pub struct Attribute {
    name: *mut c_char,
	r#type : i32,
	length : i32
}

#[repr(C)]
pub struct MetaData {
    name: *mut c_char,
    r#objectid : i32,
    r#count : i32,
    attributes : [*mut Attribute]
}

#[repr(C)]
pub struct MetaDataList {
    r#count : i32,
    metadatalist : [*mut MetaData]
}

When I call the function I see the handle is the correct on the calling side, however, when it is received in the 'C' side it is corrupted.

Calling get_metdata with handle 2023188093856 <- Rust

Rebar_Get_MetaData - Handle 584424880536 <- 'C'

If I change the return type from * const MetaDataList to u64 it works (but I canot cast u64 return into a * const MetaDataList).

Does anyone have any insight?

Thanks! I think I am there now.

1 Like

The type [array_element_t] is not #[repr(C)], you need a type that specifies how the length of the array is handled:

  • static / constant length

    • inline fixed-size array

      array_element_t elems[LEN]
      

      becomes in Rust:

      elems: [array_element_t; LEN]
      
    • pointer to fixed-size array

      array_element_t const (*array_ptr)[LEN];
      // i.e.
      typedef array_element_t const array_t[LEN];
      array_t * array_ptr;
      

      becomes in Rust:

      array_ptr: *const [array_element_t; LEN],
      
  • runtime / dynamic length: pointer to (start of) array (with length)

    array_element_t const * elems;
    size_t elems_count;
    

    becomes in Rust:

    elems: *const array_element_t,
    elems_count: usize,
    
    • Which you can convert into a usable Rust slice with:
      let elems: Option<&[array_element_t]> = unsafe {
          if elems.is_null() {
              None
          } else {
              Some(::core::slice::from_raw_parts(elems, elems_count))
          }
      };
      
3 Likes

Thankyou!!!