Call C function with an array of struct as parameter

I have a C function which requires a pointer to an array of C struct:

int query_info_from_c(port_info_t *info, uint32_t buflen, uint32_t timeout_ms);

typedef enum {

typedef struct {
	port_t port;
	uint8_t inserted;
	uint8_t addr;
} port_info_t;

I tried the following, but got segmentation fault:

#[link(name = "mylib")]
extern "C" {
    fn query_info_from_c(info: *const I2CPortInfo, buflen: u8, timeout_ms: u32) -> usize;

pub fn myquery() -> Result<Vec<I2CPortInfo>, Error> {
    let info_list = [I2CPortInfo::default(); 6];
    let buflen = 6 * mem::size_of::<I2CPortInfo>() as u8;
    let timeout_ms = 8000;
    let ret = unsafe { query_info_from_c(info_list.as_ptr(), buflen, timeout_ms) };

    if ret == 0 {
    } else {
        Err(HalError::QueryError(String::from("i2c"), ret))?

pub struct I2CPortInfo {
    port: Port,
    inserted: u8,
    addr: u8,

// Code for Port and DevType struct definitions omitted

Some things I notice:

  1. Are you sure that buflen should be the number of bytes, and not just six?
  2. You are casting buflen to an u8, but query_info_from_c takes an uint32_t.

I made an error when editing code before posting, buflen in query_info_from_c is indeed uint8_t.

I think buflen should be the size of the array, equivalent to the following C code:

i2c_port_info_t i2c_port_info[6];
buflen = sizeof(i2c_port_info);

I finally figured it out. The problem is that mylib was compiled with the -fshort-enums flag, so C and Rust enums have different size. Problem solved after I changed the annotation of Port from #[repr(C)] to #[repr(u8)]

#[repr(u8)]  // Use u8 so it matches with C enum size
pub enum Port {
1 Like

Oh yes, that compilation flag is the bane of FFI:

Screenshot 2020-08-07 at 14.02.59

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.