2

I am working with a c api with automatically generated bindings by Bindgen. One of the property structs defined by the rust wrapper takes a [i8, 256], which needs to be a string such as "mystr"

The C declaration is something like this:

    typedef struct Properties 
    {
        ...
        /** Vendor name used to identify specific hardware requested */
        char name[MAX_NAME];
        ...
    }

The binding that Bindgen creates looks like this:

    pub struct Properties
    {
        ...
        pub name: [::std::os::raw::c_char; 256usize],
        ...
    }

My code needs to fill this field somehow. I experimented with something like this:

    //property.name is of type [i8; 256]
    property.name = *("myName".as_bytes()) as [i8; 256];

which results in an error like this:

non-primitive cast: `[u8]` as `[i8; 256]`
an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object

This should probably actually be something more like a string copy function. My current solution which seems to work, but isn't very clean, is defining a function to loop through the slice and put its characters into the array one by one. There has to be a better way to do this :

    pub fn strcpy_to_arr_i8(out_arr: &[i8; 256], in_str: &str) -> Result<(), SimpleError> {
        if in_str.len() > 256 {
            bail!("Input str exceeds output buffer size.")
        }

        for (i, c) in in_str.chars().enumerate() {
            out_arr[i] = c as i8;
        }   
        Ok(())
    }

1 Answers1

4

Rust has byte literals, so "myName".as_bytes() is unnecessary. b"myName" will give you a byte array (note the initial b).

But these literals are unsigned bytes (u8). Since you seem to need signed bytes, you need to convert them: (*b"adfadsf").map(|u| u as i8).

It's also likely that a C-string would have to be 0-terminated. Do get this, you would need to add a \0 explicitly at the end of your string.

mcarton
  • 27,633
  • 5
  • 85
  • 95
  • 1
    The null termination is very important, and would likely also need to factor in to the length check, depending on how the C function works internally. – Herohtar Jan 26 '22 at 19:55
  • This seems like it's just about exactly what I need. however, it does still give me an error `expected an array with a fixed size of 256 elements, found one with 5 elements` And yes. I will HAVE to have null termination as well. That's a good catch! – Edward Pingel Jan 26 '22 at 20:51
  • 1
    to fix the length problem, you can use something like [`slice::fill()`](https://doc.rust-lang.org/std/primitive.slice.html#method.fill) to pad your string with `0u8` – Jeremy Meadows Jan 26 '22 at 21:50