0

I'm trying to write a Rust wrapper over the C++ library, where the pointer to array of C-style string is defined as:

char ***name;

In C++ I can iterate over strings easily:

for(int i=0;i<n;++i)
        std::cout << std::string(*(name[i])) << std::endl;

which gives correct results.

However, in rust I can't figure out how to do this correctly.

The bindings to C++ library are generated by bindgen correctly and name is presented as

pub name: *mut *mut *mut ::std::os::raw::c_char

So, I'm trying the following:

// Get a slice of pointers to strings of size n, n is known and is correct
// The type of names is &[*mut i8]
let names = unsafe{ std::slice::from_raw_parts(*(name), n) };
// Iterates over a slice. ptr: &*mut i8
for ptr in names.iter() {   
  // Constructs a c-string from *mut i8 
  let cs = unsafe{ CStr::from_ptr(*ptr) };
  // Shoud print a string, but gives rubbish instead
  println!("{:?}",cs);
}

But this prints rubbish and all lines of output are the same and of size 2, while the strings are different and of different length:

"\x90\""
"\x90\""
"\x90\""
"\x90\""
"\x90\""
"\x90\""

I'm definitely missing something obvious, but can't find any errors. Any help is appreciated!

yesint
  • 145
  • 9

1 Answers1

0

Your rust code seems correct. Here is a simulation of the situation, with the memory layout matching your description.

Maybe the actual layout on the C side is not exactly what you described. In my opinion, the first level of reference (pointer) is not needed, but I don't know the actual need for your use case.

use std::ffi::{c_char, CStr};

fn prepare_name_vec() -> Vec<*const c_char> {
    ["one\0", "two\0", "three\0", "four\0"] // static nul-terminated strings
        .iter()
        .map(|s| s.as_ptr() as *const _) // u8 ptr to u8 ptr
        .collect()
}

fn read_names_from_c(
    names: *const *const *const c_char,
    n: usize,
) {
    /*
    names         *names        c_str
    char *** •~~> char ** •~~> [char *   •~~> ['o', 'n', 'e', '\0']
                               ,char *   •~~> ['t', 'w', 'o', '\0']
                               ,char *   •~~> ['t', 'h', 'r', 'e', 'e', '\0']
                                char * ] •~~> ['f', 'o', 'o', 'r', '\0']
    */
    let name_slice = unsafe { std::slice::from_raw_parts(*names, n) };
    for &c_ptr in name_slice.iter() {
        let c_str = unsafe { CStr::from_ptr(c_ptr) };
        println!("{:?}", c_str);
    }
}

fn main() {
    let name_vec = prepare_name_vec();
    let names: *const *const *const c_char = &name_vec.as_ptr() as *const _;
    let n = name_vec.len();
    read_names_from_c(names, n);
}
prog-fh
  • 13,492
  • 1
  • 15
  • 30