Reputation: 51
I'm trying to interact with a low-level C API using Rust. It requires calling to get the size of an array, then calling again with an allocated array of structs to match that size, function looks like
getVals(pNumItems: *mut usize,
pItems: *mut)
per the API usage, I'm doing this:
let mut numItems: usize = 0;
getVals(&numItems,
ptr::null_mut());
// Now we know count, so make an array of MyInfo structs and call again:
let itemInfo: Vec<MyInfo> = Vec::with_capacity(numItems);
getVals(&numItems, itemInfo.as_mut_ptr());
But the first call can't compile because I get error message:
mismatched types expected raw pointer
*mut u32
found reference&usize
let mut items
I'm unclear what the difference is here. I tried casting it to *mut u32 but that complains about it being unsafe. This whole block of code is being done inside
unsafe{}
So, I'm thinking I'm not doing this correctly. Is there a better way to do this?
My vector creation and usage doesn't have compile-time errors, but I'm not sure if that is the expected way to do this?
Upvotes: 4
Views: 1329
Reputation: 16805
You probably want to do something like this.
#[repr(C)]
struct MyInfo {
a: i32,
b: f32,
}
extern "C" {
fn getVals(
pNumItems: *mut usize,
pItems: *mut MyInfo,
);
}
fn main() {
// first detect required size
let mut num_items: usize = 0;
unsafe { getVals(&mut num_items, std::ptr::null_mut()) };
// then allocate storage and get items
let mut item_info: Vec<MyInfo> = Vec::with_capacity(num_items);
unsafe { getVals(&mut num_items, item_info.as_mut_ptr()) };
unsafe { item_info.set_len(num_items) };
}
The #[repr(C)]
annotation ensures the layout of the structure is compatible with C.
Your function must be declared as extern "C"
: its definition is not provided by Rust, and no mangling is applied to the symbol name.
When calling such an extern function, an unsafe
block is required (because the Rust compiler cannot guaranty anything about the safety of this call).
In order to obtain a *mut
pointer to num_items
you need to start from a &mut
reference (not a &
which could only provide a *const
pointer).
After the call has stored data into the allocated buffer, you need to adjust the length of the vector because it does not know what you have done with its allocated storage (of course this is unsafe; you are the only one to know this is correct).
And if the compiler was complaining about *mut u32
vs &usize
, it's probably because you declared pNumItems: *mut u32
in getVals()
.
If you really want this (because on the C side uint32_t
is expected), then you need to declare let mut num_items: u32 = 0;
at call site and use Vec::with_capacity(num_items as usize)
and .set_len(num_items as usize)
.
Upvotes: 3