Unable to get char** into Rust

Hello good people,

I have a code like this:

#[repr(C)]
pub struct SomeStruct {
pub detected_device_names: c_char**,
pub dev_name_len: i32,
};

Then I use the same struct which is filled by a C-function and returned to Rust

let mut dev_info = get_list_from_native();

Now, the problem that I am facing is: I am unable to iterate over the detected_device_names !
I have naively tried

for i in 0..dev_info.detected_device_len {
let a_device = dev_info.detected_device_names[i];
}

But it gave me error : Cannot index into a value of type *mut *mut u8
If I get some help about it, it would be great. Thank you.

The (unsafe) API for "indexing" into a pointer is to use the .add(i) method for pointers and dereference the result. The example code in the linked documentation also helps illustrate the exact way this would look. Rust is known to be a bit less ergonomic to work with when you use with (unsafe) raw pointers, so indexing syntax isn't directly supported. Another option could be to build a &mut [*mut u8] slice first, then indexing and even directly iterating would work as expected. Building such a slice could be achieved via from_raw_parts_mut in std::slice - Rust.

3 Likes

Just as a heads up, this is not valid rust syntax.
It would have to look something like this

use core::ffi::c_char;

#[repr(C)]
pub struct SomeStruct {
    pub detected_device_names: *mut *mut c_char,
    pub dev_name_len: i32,
}
1 Like

Operating raw pointer is complicated, is it possible Rust owns detected_device_names, and provide a buffer for C to write in?


I have no idea, could someone tell me?

Yes Sebastian, I have had written the C struct first but at the last moment, changed my mind and re-wrote it in Rust syntax. The remnant is visible.
Thank you for pointing it out, I will be more careful next time.

So my code should look like this ?

for i in 0..dev_info.detected_device_len {
    let mut rptr = ptr::slice_from_raw_parts_mut(dev_info.detected_device_names, i);
    /* do some work */
}

Does above code looks Ok? I am doubtful. Any suggestions?

Thank you.

What I thought of was something like

for a_device in slice::from_raw_parts_mut(
    dev_info.detected_device_names,
    dev_info.detected_device_len.try_into().unwrap(),
) {
    // ... 
}

Arguably, depending on the use-case, you could also be working with an immutable slice, so which would like the same but without the _mut ending in the method name. As it stands above, the type this loop gives is &mut *mut u8 (Assuming detected_devicd_names is a *mut *mut u8.) Which you can dereference as needed to read (or possibly even replace by something else) one of the *mut u8s. How to work with those, presumably zero-terminated strings of some (not specified here) encoding, is then the next step that I haven't discussed yet.

Given this Rust to C interface, I also wonder what your ownership story looks like, and who will, in what manner, clean up the allocations that would likely be involved in this example.

2 Likes

I couldn’t agree more. BTW is there any elegant way for safety allocation across FFI?, like Rust take over the C-String allocated by C functions, or some else.

Hello Steffahn,

To answer your question, it is guaranteed that:

  1. Allocations happen in C layer.
  2. They stay up for the entire lifetime of the program.
  3. Rust can choose to own up the allocated memory as it is going to replace the functionality of the C layer designed earlier to work on that memory.

Steffahn,

I have tried using your code and the compiler didn't like it
Error that I got is:

*mut [*mut u8] is not an iterator
help: the trait `std:iter::Iterator is not implemented for `*mut [*mut u8]`

I am sorry but my knowledge is still very limited and I am unable to solve this problem on my own.
Please help.

Thank you.

There's a difference between std::ptr::slice_from_raw_parts_mut and std::slice::from_raw_parts_mut :wink: The error indicates you might have used the ptr one.

Ah, so clumsy of me.
Right now the code compiled but I am getting raw addresses instead of the data.

for a_device in slice::from_raw_parts_mut(
    dev_info.detected_device_names,
    dev_info.detected_device_len.try_into().unwrap(),
) {
    println!("dev_info = {:#?}", **a_device);
}

By the way, if you only want to read anyways, then you could use slice::from_raw_parts to create an immutable slice, too. If the Rust code is only supposed to read the data anyways, you could work with *const *const u8 in the first place, too, to make that more clearly documented.

The next step to read the actual string data could be to use something like CStr::from_ptr. Having used u8 as a stand-in for C’s char, to make using CStr::from_ptr easier to use, you could also work with *const *const i8 from the start.

I’ve written a small example with a test case here: Rust Playground

Why did you write your own unwrap function? Doesn't it do the same as the unwrap method?

EDIT

Ah I see, the unwrap method can't be used in a const context.

Thank you Steffahn, the sample code was extremely helpful. I am able to get the data that I require.

1 Like

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.