you can have an DST in rust for similar functionality. since it's a DST, you have a fat pointer, so the trick is convert the rust fat pointr to an ffi compatible thin pointer. for memory safety, you must guarantee the information of the pointer metadata (the len of the array in this case) is not lost, and it can be correctly recovered.
the concept is simple:
#[repr(C)]
struct WithLen<Data: ?Sized> {
len: usize,
/// ?Sized type must be the last field of the struct
data: Data,
}
/// the fixed header
type MyStringHeader = WithLen<[u8; 0]>;
/// payload with a static size
type MyStringSized<const N: usize> = WithLen<[u8; N]>;
/// payload static type is erased
type MyStringUnsized = WithLen<[u8]>;
// construct a value with Sized payload
let s: MyStringSized = Box::new(WithLen::new([65, 66, 67, 69]));
// cocerce into an unsized payload, this is a fat pointer
let s: Box<MyStringUnsize> = s;
// cast to a thin pointer and strip the metadata away
// suitable to be passed to ffi
let s = Box::into_raw(s) as *const MyStringHeader;
// crucial, must implement correctly
unsafe fn my_string_len(s: *const MyStringHeader) -> usize { ... }
// how to correctly drop the object:
let len = unsafe { my_string_len(s) };
let fat_s = unsafe { std::ptr::slice_from_raw_parts(s as *const u8, len) };
let _ = unsafe { Box::from_raw(fat_s as *mut MyStringUnsized) };
this example first creates a statically sized value then unsize coerce it into a DST. if your app must create the DST based on runtime knonw size, it's more involved. I recommend the fambox crate. see also this earlier thread: