Creating fixed size slice from pointer passed from C

Hi all,
I have a simple Rust function that exposed to C. I want to create a fixed size slice from a pointer in C. Here is the snippet:

#[no_mangle]
pub extern "C" fn handle_packet(pkt_ptr: *const u8, pkt_len: size_t)  {
    let packet: &[u8; 64] = unsafe {
        assert!(!pkt_ptr.is_null());
        assert!(pkt_len == 64);
        slice::from_raw_parts(pkt_ptr, pkt_len as usize)
    };
}

But I got the following error:

   |
25 |         slice::from_raw_parts(pkt_ptr, pkt_len as usize)
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected array `[u8; 64]`, found slice `[u8]`
   |
   = note: expected reference `&[u8; 64]`
              found reference `&[u8]`

TryInto can be used here to convert the slice into an array ref.

It returns a result because it needs to check the size, so you'll have to unwrap it, but since you already asserted the size, it shouldn't be an issue.

@Gilnaa Thanks for your prompt response.

Since the array has statically known size, its reference is not consists of the fat ptr. You can replace the slice::from_raw_parts() call with &*(pkt_ptr as *const [u8; 64])

5 Likes

Even better, do not put non-unsafe functions in an unsafe block, and do not duplicate the length-checking part, either. Furthermore, libc::size_t is guaranteed to be the same as usize so you can use usize in FFI without a cast:

#[no_mangle]
pub extern "C" fn handle_packet(pkt_ptr: *const u8, pkt_len: usize)  {
    assert!(!pkt_ptr.is_null());
    let s = unsafe { slice::from_raw_parts(pkt_ptr, pkt_len) };
    let packet: &[u8; 64] = s.try_into().expect("invalid size");
}
2 Likes

Thanks for your suggestion. I changed the function definition to:

#[no_mangle]
pub extern "C" fn handle_packet(pkt_ptr: &[u8; 64])  {
    do_some_process(pkt_ptr);
}

Is this the correct way of passing fixed size array from C to Rust?

No, that is incorrect.

Thanks for your reply. Would you please explain more?

Arrays are not FFI-safe, and even if they were, an &[T; N] isn't represented as a (pointer, length) pair of arguments since the length is implicit. Even slices can't be passed like this, because the slice is equivalent with a (pointer, length) pair which is not necessarily the same ABI-wise as passing separate pointer and length arguments.

Thanks a lot for the explanation :pray:.