FFI: Access c struct from Rust

Hi,

Probably a very simple problem but i am stuck. I am trying to access structure elements in c code from rust. What i have is the following little example:
Project source tree:

Cargo.toml
build.rs
src/
├── test.cpp
├── test.hpp
└── main.rs

main.rs

extern crate libc;
use libc::{size_t};

#[repr(C)]
#[derive(Debug,Copy, Clone)]
pub struct Bt {
    opaque: [*const u8; 0]
}

extern "C" {
    pub fn bt_del(idx: *mut Bt) -> bool;
    pub fn bt_make(size: size_t) -> *mut Bt;
}

struct Index{
    bt: *mut Bt,
    req: Vec<u8>,
}

impl Index {
    pub fn new()->Self{
        Index{
            bt: Index::make(10 as u32),
            req: Vec::new(),
        }
    }

    pub fn make(x:u32) -> *mut Bt{
        unsafe {
            bt_make(x as  size_t)
        }
    }
}

fn main() {
    let mut idx = Index::new();
    println!("{:?}", (*idx.bt).size);
}

builder.rs

const FILES: &[&str] = &[
    "src/test.cpp",
];
const HEADERS: &[&str] = &[
    "src/test.hpp",
];

fn main() {
    for file in FILES {
        println!("cargo:rerun-if-changed={}", file);
    }
    for file in HEADERS {
        println!("cargo:rerun-if-changed={}", file);
    }
    cc::Build::new()
        .define("COMPILATION_TIME_PLACE", "\"build.rs\"")
        .warnings(false)
        .extra_warnings(false)
        .files(FILES)
        .flag("-fPIC")
        .compile("bt.a");
        println!("cargo:rustc-link-lib=z");
        println!("cargo:rustc-link-lib=m");
        println!("cargo:rustc-link-lib=stdc++");
}

test.cpp

#include "test.hpp"
extern "C" {
  bt_t *bt_make(int size)
  {
  	bt_t *bt;
    bt = (bt_t*)calloc(1, sizeof(bt_t));
    bt->bt = (uint32_t*)calloc(size, 4);
    bt->size = size;

    for (int i =0; i < size;i++){
      bt->bt[i] = i*2;
    }

  	return bt;
  }

  bool bt_del(bt_t *bt){
    if (bt == 0) return false;
  	free(bt->bt);
  	free(bt);
    return true;
  }
}

test.hpp

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>

typedef struct  {
  uint32_t* bt;
  uint32_t  size;
} bt_t;

Error:

error[E0609]: no field `size` on type `Bt`
  --> src/main.rs:47:33
   |
47 |     println!("{:?}", (*idx.bt).size);
   |                                 ^^^^ unknown field
   |
   = note: available fields are: `opaque`

For more information about this error, try `rustc --explain E0609`.

Did i miss something or did i completely fail to understand FFI and rust?

Thank you so much for your help!

Your code almost works; you just need to change the definition of Bt to match bt_t as defined in C++.

--- a/src/main.rs
+++ b/src/main.rs
@@ -4,7 +4,8 @@ use libc::{size_t};
 #[repr(C)]
 #[derive(Debug,Copy, Clone)]
 pub struct Bt {
-    opaque: [*const u8; 0]
+    bt: *mut u32,
+    size: u32,
 }
 
 extern "C" {
@@ -34,5 +35,5 @@ impl Index {
 
 fn main() {
     let mut idx = Index::new();
-    println!("{:?}", (*idx.bt).size);
+    println!("{:?}", unsafe { (*idx.bt).size });
 }

Also, if a Bt object is logically responsible for freeing the memory in its bt pointer, it may be better not to declare the type as Copy and Clone, since otherwise you could easily end up freeing bt multiple times.

1 Like

In order not to unnecessary copy my example, an additional question now is, how would i gain then access to the array. Ints now i can access, how would i iterate through bt?

Example of what i would like to do :

let x = unsafe { (*idx.bt).size };
for  i in 0..x {
   println!("value:{:?} ",(*(*idx.bt).bt)[i] );
}

The cleanest is probably to define a function that gives you a slice into the memory:

fn bt_to_slice(bt: &bt_t) -> &[u32] {
    unsafe {
        std::slice::from_raw_parts(bt.bt, bt.size as usize)
    }
}

Then you can iterate over it:

for value in bt_to_slice(&idx) {
}

But you can also use pointer addition to get a pointer to the element, then dereference it:

let x = unsafe { (*idx.bt).size };
for  i in 0..x {
   println!("value:{:?} ", *idx.bt.add(i));
}
2 Likes

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.