How to copy bytes into a c_char array?

Hi,

I cannot seem to find an easy way to copy bytes into a c_char array needed for a FFI. The following code (playground) fails to compile, because c_char is i8, but b"..." turns into u8:

fn main() {
    let c_char_array: [std::os::raw::c_char; 16] = [0; 16];
    c_char_array.copy_from_slice(&b"hello, world\0"[..]);
}

What could be an idiomatic solution?

The most idiomatic solution without using other crates (Which I'm omitting due to my unfamiliarity with this in the crate ecosystem) would be as follows:

fn main() {
    let mut c_char_array: [std::os::raw::c_char; 16] = [0; 16];
    for (dest, src) in c_char_array.iter_mut().zip(b"hello, world\0".iter()) {
        *dest = *src as _;
    }
}

It seems you're dealing with C Strings, which Rust can deal with using std::ffi::CString.

If you want to use unsafe, perhaps the following?

let mut c_char_array: [std::os::raw::c_char; 16] = [0; 16];
unsafe {
    *(&mut c_char_array as *mut _ as *mut [u8; 13]) = *b"hello, world\0";
}

Playground

Thanks, I already thought about the iterator for loop approach, but would have guessed that there is an even easier way.

I had also tried std::ffi:{CStr,CString} before, but they also turn into [u8], so they have the same issue as my initial code.

The [c_char] buffer is part of a larger data struct in a C library that users of the C library are usually initialise by copying bytes into via memcpy and friends.

mind that this is architecture dependent; for example on ARM and RISC-V c_char is u8, which makes them finicky to work with

Thanks, I already thought about the iterator for loop approach, but would have guessed that there is an even easier way.

a loop is easiest and most portable in this case, i think

Since that's the case, I'd also suggest noting the importance of using as _ instead of as i8 when using a loop.

For example:

#[cgf("windows")]
type Foo = i8;
#[cfg("unix")]
type Foo = u8;

fn foo(x: char) -> Foo {
    x as i8
}

Would compile on windows but not on macos or linux.
If we instead said either:

fn foo(x: char) -> Foo {
    x as _
}

Or

fn foo(x: char) -> Foo {
    x as Foo
}

Then it would be completely portable.

I personally go the other way around, I interpret c_chars as u8s, given how most of the Rust APIs use u8s (such as CStr and stuff):

fn main ()
{
    let mut c_char_array: [u8; 13] = [0; 13];
    c_char_array
        .copy_from_slice(&b"hello, world\0")
    ;
}

The other option is to use a little bit of unsafe code, to get the valid
&mut [c_char] -> &mut [u8]
cast:

fn main ()
{
    let mut c_char_array: [c_char; 14] = [0; 14];
    c_char_array
        .as_mut_bytes()
        .copy_from_slice(&b"Hello, World!\0"[..])
    ;
}

The unsafe code can either be written directly (Playground), or used through a helper crate, such as
::safe_transmute::to_bytes::transmute_to_bytes_mut::<c_char>()