Search code examples
c++rustffi

How to convert rust string to i8(c_char) array?


I'm using rust to call c++, there are structs generated by bindgen and cc. The struct fields type is like [i8;11] or [i8;n]. I want to set these fields. How to write a common function to transform rust String/CStr/CString to i8/c_char array?


Solution

  • There are two problems you have to be aware of:

    • null terminations: Rust strings aren't null-terminated, C/C++ strings are.
    • Encoding: Rust strings are UTF-8 encoded, C/C++ string encoding depends on the operating system. On most systems, C/C++ strings are also UTF-8, but on Windows, it is most likely something else, like Windows-1252. In that case you need an external library like encoding_rs to convert the encoding properly.

    Let's assume C/C++ strings are also encoded in UTF-8 (again, an assumption you can not make on Windows). Then, you can convert it (for example) like that:

    use std::ffi::CString;
    
    fn main() {
        // Fill with dummy data to 'simulate' the situation of uninitialized memory
        let mut c_array = [42i8; 11];
    
        // The data that should get written into c_array
        let rust_string = String::from("Hello!");
    
        // Convert to CString, required for FFI
        let rust_cstring = CString::new(rust_string).unwrap();
        // Extract null-terminated raw data
        let byteslice = rust_cstring.as_bytes_with_nul();
    
        // Write data. Note that this will panic if `byteslice` is longer than `c_array`.
        let mut byteslice_iter_i8 = byteslice.iter().map(|v| *v as i8);
        c_array[0..byteslice.len()].fill_with(|| byteslice_iter_i8.next().unwrap());
    
        println!("{:?}", c_array);
    }
    
    [72, 101, 108, 108, 111, 33, 0, 42, 42, 42, 42]
    

    There are of course other ways, and this one probably isn't the absolute single best way to do that, but I hope that this managed to demonstrate the basic principle.