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?
These types make it abundantly clear that the values are meant for interfacing to C code. That alone is useful.
Rust may be ported to a platform where those types are different (perhaps in some very subtle ways), so better use them.
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.
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:
-
Is Rust realistically going to be ported to platforms where
sizeof(size_t) != sizeof(void*)
? -
If yes, how could
size_t
realistically differ? I.e. what kind of failure modes couldlibc::size_t as usize
orusize as libc::size_t
cause?
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
:
ring/c.rs at master · briansmith/ring · GitHub. 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
.
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.
Thanks. This is what I wanted to hear.
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.
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.
Also, I guess this should get replicated into the core
module so that no_std
crates can use them.