FFI: fill char array from rust so that it is visable in c

Hi,

I have another FFI conundrum i am trying to figure out. I want to fill an array of structs from rust so that i can directly access them in c. Example:

src tree:


build.rs
src - main.rs
    - test.cpp
    - test.hpp
main.rs
-------------------------------------
extern crate libc;
use libc::{c_char, c_int, size_t};

#[repr(C)]
#[derive(Debug)]
pub struct Arr {
  str  : *mut i8,
  size : u32,
}

#[repr(C)]
#[derive(Debug)]
pub  struct  Rec {
  rarr: *mut Arr,
  count : u32,
}

extern "C" {
    pub fn printer() -> bool;
}

fn main() {
    // how do i fill  char array from rust so that i can use printer to print it in c
}
test.hpp
-------------------------------------------
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <stdint.h>

typedef struct  {
  char*     str;
  uint32_t  size;
} record_t;


typedef struct  {
  record_t* rarr;
  uint32_t  count;
} rarray_t;


extern rarray_t * ExtData;

// abi
extern "C" {
 bool printer ();
}

test.cpp
--------------------------------------------
#include "test.hpp"

extern "C" {
  bool printer(){
    for (i = 0; i< ExtData->count; i++){
      cout << i << ": ";
      for (j = 0; j< ExtData->rarr->size; j++){
        cout <<  ExtData->rarr->str[j] << "";
      }
      cout<< endl;
    }
    return true;
  }
}

Or is there a better way to do this? So what i am doing right now is filling multiple vectors on rust side and passing them using functions to c and then refilling the two structures in c. data needs to be in c structures like described because i do not want to go changing the entire c code ... also once it is printed it needs to be destroyed from rust side . can this be done ?

Thank you ! Please do not hold it against me for maybe asking trivial question. I still cannot judge if they are trivial or not :frowning:

If the memory layout of the C data structure is different from what you are using in Rust (likely), then you can't do this without copying over the data from the Rust types and into the appropriately-typed C types.

It would be a massive code quality improvement to use function arguments instead of global "god objects", though. Also, run your Rust code through rustfmt and clippy to make its formatting idiomatic and to check for potential mistakes.

1 Like

Use c_char, not i8. c_char is u8 on some platforms.

If you're working with Vec<String> or Vec<Vec<u8>> or the like, then yes, you're going to have to translate that into a Vec<Arr> before passing it to C (by constructing a Rec) [1]. Your data-owning Rust types should be compatible with whatever "filling" the C is doing (e.g. don't use String if it might not write UTF8).

If you include the Rust code you're using now, you may get a more thorough review.


  1. or the other way around; your Rust names seem to be the opposite of your C names? ↩ī¸Ž

1 Like