LalaWootwoot
LalaWootwoot

Reputation: 51

Using Rust with C API and mut/pointer problems with types

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

Answers (1)

prog-fh
prog-fh

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

Related Questions