I am passing data from Rust to C. While passing primitives seems to be easy, I am kind of lost with structs.
I have the following Rust code:
use ::std::os::raw::*;
static NAME: &[c_char] = &[65, 66, 67, 0];
#[repr(C)]
pub struct MyStruct {
pub x: c_int,
pub y: *const c_char,
}
#[no_mangle]
pub extern "C" fn get_my_struct() -> *const MyStruct {
let my_struct = MyStruct {
x: 11 as c_int,
y: NAME.as_ptr(),
};
unsafe {
::std::mem::transmute(Box::new(my_struct))
}
}
And the following C code:
typedef struct _my_struct my_struct;
extern const my_struct get_my_struct(void);
struct _my_struct {
int x;
const char *y;
};
int main(void) {
my_struct my_complex_struct = get_my_struct();
return 0;
}
The output from gdb says:
(gdb) p my_complex_struct
$1 = {x = 6295568, y = 0x7ffff7bce1e0 <ref> "ABC"}
The string looks fine but the int is definitely off. What am I missing here? Why is the value of x 6295568 and not 11?
Compiled by:
gcc (Debian 4.9.2-10) 4.9.2
rustc 1.20.0-nightly (8d22af87d 2017-07-22)
cargo 0.21.0-nightly (ffab51954 2017-07-18)
using:
cargo build
gcc --std=c11 -g -o main src/main.c /test/target/debug/libtest.so -L target/debug/
You have an issue because your ABI doesn't match. You are returning a pointer to an allocated structure but your C code claims the function returns a struct directly.
As demonstrated in The Rust FFI Omnibus chapter on objects, you should use Box::into_raw
:
#[no_mangle]
pub extern "C" fn get_my_struct() -> *const MyStruct {
let my_struct = MyStruct {
x: 11 as c_int,
y: NAME.as_ptr(),
};
Box::into_raw(Box::new(my_struct))
}
Your C function should be marked as returning a pointer:
extern const my_struct *get_my_struct(void);
// ...
int main(void) {
const my_struct *my_complex_struct = get_my_struct();
// ...
}
(lldb) p *my_complex_struct
(my_struct) $1 = (x = 11, y = "ABC")
The code also has a memory leak; you need to return the pointer back to Rust so it can be properly deallocated.
If you meant to return the struct directly, change your Rust code to not perform an allocation:
#[no_mangle]
pub extern "C" fn get_my_struct() -> MyStruct {
MyStruct {
x: 11 as c_int,
y: NAME.as_ptr(),
}
}
(lldb) p my_complex_struct
(my_struct) $0 = (x = 11, y = "ABC")
Disclaimer: I'm the primary author of the Omnibus.