Are libc declarations useful for obviously-sized primitive types?


#1

I understand that using libc::c_int, which doesn’t have a cross-platform standard size, makes sense when calling into legacy C code that declares int. However, when exposing Rust code to C, is there any good reason to bother using e.g. libc::uint32_t instead of just writing u32 on the Rust side or libc::size_t instead of just writing usize on the Rust side?


#2

These types make it abundantly clear that the values are meant for interfacing to C code. That alone is useful.


#3

Rust may be ported to a platform where those types are different (perhaps in some very subtle ways), so better use them.


#4

Personally, I always use u32 and friends where the corresponding C interface uses uint32_t and friends. However, I always use libc::size_t and ssize_t.


#5

If a platform’s int32_t differs from i32, things are seriously broken. (C even requires intN_t to be two’s complement even though int is allowed to be one’s complement.)

So the issues really are:

  1. Is Rust realistically going to be ported to platforms where sizeof(size_t) != sizeof(void*)?

  2. If yes, how could size_t realistically differ? I.e. what kind of failure modes could libc::size_t as usize or usize as libc::size_t cause?


#6

You can use usize instead of libc::size_t everywhere, as libc::size_t is defined as a synonym of usize in libc for every currently-supported platform. Same with isize and ssize_t. I recommend you never use libc::size_t and libc::ssize_t.

In theory there could be a platform where size_t is 16 bits and pointers are 32 bits, or where size_t is 32 bits and pointers are 64 bits. However, you will probably never encounter any such platform when programming in Rust. (I don’t know of any such platform.)

Similarly, I recommend you never use the libc::uintN_t and libc::intN_t types. Instead, use the native Rust types.

In particular, I recommend that you avoid adding a dependency to the libc crate unless you actually use a libc function. And, you basically should never use a libc function.

I developed my own module for this in ring in ring::c:
https://github.com/briansmith/ring/blob/master/src/c.rs. Note that I also have tests to verify that the C compiler’s alignment and sizeof agree with Rust’s. My version of libc::c_int is ring::c::int.


#7

That kind of thing happens with segmentation, e.g. 16-bit Windows (32 bit “far” pointers, but malloc() objects are limited to 64K, so size_t can still be 16-bits). Hopefully Rust may never see such a system, popular though they once were.


#8

Thanks. This is what I wanted to hear.


#9

There’s also std::os::raw which is a subset of libc (the primitive types). If only what’s there is needed, I prefer to skip the libc crate entirely.


#10

Thanks. I never knew that module existed.

I noticed the documentation is “Raw OS-specific types for the current platform/architecture.” I guess in a build script “current” is “host” and otherwise it is “target”.

NIT: It’s too bad that all of these have the unnecessary c_ prefix.


#11

Also, I guess this should get replicated into the core module so that no_std crates can use them.