s3rvac s3rvac - 7 months ago 35
C Question

How to handle passing usize to a C function that expects uint32_t?

I am writing a Rust wrapper around a C library. The C library provides the following function:

void lib_foo(const unsigned char *buf, uint32_t buf_len);

I have created the following

fn lib_foo(buf: *const u8, buf_len: u32);

and the following high-level wrapper:

pub fn foo(buf: &[u8]) {
unsafe { lib_foo(buf.as_ptr(), buf.len() as u32) }

However, the cast from
(the type of
) to
may cause the size to be truncated. What is the best way of dealing with this?


First of all, please use the libc crate for types.

That is: extern fn lib_foo(buf: *const libc::c_uchar, buf_len: libc::uint32_t);

While it is slightly longer, it will avoid making assumption (unsigned char maps to u8) and makes the translation automatic.

Then, onto the high level wrapper.

Without any assumption, the simplest solution is to panic, and document this.

/// Calls `lib_foo`, ...
/// # Panics
/// If the size of the buffer is strictly greater than 2^32-1 bytes.
pub fn foo(buf: &[u8]) {
    assert!(buf.len() <= (std::u32::MAX as usize));

    unsafe { lib_foo(buf.as_ptr() as *const _, buf.len() as libc::uint32_t) }

Then, depending on the domain, other options may arise:

  • saturation may be an option (using std::cmp::min)
  • repeated calls may be another
  • ...

in any case, if the difference of behavior is observable to the user, document.