Using struct with boxed array in FFI

I have the following code:

#![allow(dead_code)]

#[repr(C)]
struct BoxedArray {
    arr: Box<[u8; 42]>
}


extern "C" {
    fn uses_boxed_array(arr: *mut BoxedArray);
}

playground
which generates the following warning:

warning: `extern` block uses type `Box<[u8; 42]>`, which is not FFI-safe
  --> src/lib.rs:10:30
   |
10 |     fn uses_boxed_array(arr: *mut BoxedArray);
   |                              ^^^^^^^^^^^^^^^ not FFI-safe
   |
   = note: `#[warn(improper_ctypes)]` on by default
   = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
   = note: this struct has unspecified layout

I'm a little bit confused about the error message. The BoxedArray is repr(C) and the Box is ABI compatible with a [f64;42]* on the C side. I'm assuming the error comes from the array, see this note on array layouts.

I'm wondering if this is simply a false positive of improper_ctypes warning or if the code is indeed incorrect.

Note: I could store a *mut f64 in the WrappedBox by allocating a Box<[f64;42]> and calling into_raw and then use from_raw in the Drop implementation, but this seems less elegant to me than the above solution of just using Box<[f64;42]>.

It's kind of in between:

  • on the one hand, it's kind of code-smelly to have a type with drop-glue be involved in FFI boundaries;
  • on the other hand, your use case seems legitimate (assuming you are the one allocating the stuff, and not the FFI!! In that case, btw, the FFI API looks a bit weird, but maybe you've minimized the code a little bit too much).

So I wouldn't blame the lint, but I'd glady #[allow(…)]-silence it in your case :slightly_smiling_face:

Another approach would be to say that uses_boxed_array takes a &mut &mut [f64; 42], and then, when calling it, just do a &mut &mut *my_boxed_array.arr.

2 Likes

Thanks for the quick answer!

Yes, I'm the one allocating and deallocating the buffer.

Yes, I've probably minimized it too much :sweat_smile:
The BoxedArray is more like a

struct RetValue {
    double *values;
    SomeOtherStruct struct;
    // other fields
}

on the C side and the function is more of a int compute(RetValue& ret, /* other params */) so my function needs a pointer to the struct (which contains the allocated buffer) and not to the buffer directly.

So I'll just #[allow(..)] the lint. Thanks for your help! :blush:

2 Likes

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.