Using stdint.h Types
Safety standards for C - such as MISRA-B (pronounced "miserable" by many programmers) - find the C type standards as unpalattable as I do. Much OS development such as Linux also recommends specifying exactly what type you want to use. C supports this with stdint.h.
Let's modify our clib to use it:
First the header:
#pragma once
#include <stdint.h>
int8_t double_byte(int8_t n);
Then the body:
#include "clib.h"
int8_t double_byte(int8_t n) {
return n * 2;
}
Aren't you glad that Rust doesn't make you specify everything twice?
From the Rust point-of-view, this is much nicer. You can be sure that an int8_t is the same as an i8, and an uint32_t is a u32. No more figuring out what a long long int means on this platform! And we can go back to some familar Rust:
#![allow(unused)] fn main() { mod rs { pub fn double_byte(n: i8) -> i8 { n.wrapping_mul(2) } } }
If the code-base you are porting supports
stdint, you can expect to enjoy reduced mental gymnastics. My experience is split: some shops adopted it enthusiastically, some didn't. It causes less of a fight than most C standard enhancements...
There Has To Be a Downside...
Let's look at what bindgen has given us. We'll scroll for a while:
#![allow(unused)] fn main() { /* automatically generated by rust-bindgen 0.70.1 */ pub const __WORDSIZE: u32 = 64; pub const __has_safe_buffers: u32 = 1; pub const __DARWIN_ONLY_64_BIT_INO_T: u32 = 1; pub const __DARWIN_ONLY_UNIX_CONFORMANCE: u32 = 1; pub const __DARWIN_ONLY_VERS_1050: u32 = 1; pub const __DARWIN_UNIX03: u32 = 1; pub const __DARWIN_64_BIT_INO_T: u32 = 1; pub const __DARWIN_VERS_1050: u32 = 1; pub const __DARWIN_NON_CANCELABLE: u32 = 0; pub const __DARWIN_SUF_EXTSN: &[u8; 14] = b"$DARWIN_EXTSN\0"; pub const __DARWIN_C_ANSI: u32 = 4096; pub const __DARWIN_C_FULL: u32 = 900000; pub const __DARWIN_C_LEVEL: u32 = 900000; pub const __STDC_WANT_LIB_EXT1__: u32 = 1; pub const __DARWIN_NO_LONG_LONG: u32 = 0; pub const _DARWIN_FEATURE_64_BIT_INODE: u32 = 1; pub const _DARWIN_FEATURE_ONLY_64_BIT_INODE: u32 = 1; pub const _DARWIN_FEATURE_ONLY_VERS_1050: u32 = 1; pub const _DARWIN_FEATURE_ONLY_UNIX_CONFORMANCE: u32 = 1; pub const _DARWIN_FEATURE_UNIX_CONFORMANCE: u32 = 3; pub const __has_ptrcheck: u32 = 0; pub const USE_CLANG_TYPES: u32 = 0; pub const __PTHREAD_SIZE__: u32 = 8176; pub const __PTHREAD_ATTR_SIZE__: u32 = 56; pub const __PTHREAD_MUTEXATTR_SIZE__: u32 = 8; pub const __PTHREAD_MUTEX_SIZE__: u32 = 56; pub const __PTHREAD_CONDATTR_SIZE__: u32 = 8; pub const __PTHREAD_COND_SIZE__: u32 = 40; pub const __PTHREAD_ONCE_SIZE__: u32 = 8; pub const __PTHREAD_RWLOCK_SIZE__: u32 = 192; pub const __PTHREAD_RWLOCKATTR_SIZE__: u32 = 16; pub const INT8_MAX: u32 = 127; pub const INT16_MAX: u32 = 32767; pub const INT32_MAX: u32 = 2147483647; pub const INT64_MAX: u64 = 9223372036854775807; pub const INT8_MIN: i32 = -128; pub const INT16_MIN: i32 = -32768; pub const INT32_MIN: i32 = -2147483648; pub const INT64_MIN: i64 = -9223372036854775808; pub const UINT8_MAX: u32 = 255; pub const UINT16_MAX: u32 = 65535; pub const UINT32_MAX: u32 = 4294967295; pub const UINT64_MAX: i32 = -1; pub const INT_LEAST8_MIN: i32 = -128; pub const INT_LEAST16_MIN: i32 = -32768; pub const INT_LEAST32_MIN: i32 = -2147483648; pub const INT_LEAST64_MIN: i64 = -9223372036854775808; pub const INT_LEAST8_MAX: u32 = 127; pub const INT_LEAST16_MAX: u32 = 32767; pub const INT_LEAST32_MAX: u32 = 2147483647; pub const INT_LEAST64_MAX: u64 = 9223372036854775807; pub const UINT_LEAST8_MAX: u32 = 255; pub const UINT_LEAST16_MAX: u32 = 65535; pub const UINT_LEAST32_MAX: u32 = 4294967295; pub const UINT_LEAST64_MAX: i32 = -1; pub const INT_FAST8_MIN: i32 = -128; pub const INT_FAST16_MIN: i32 = -32768; pub const INT_FAST32_MIN: i32 = -2147483648; pub const INT_FAST64_MIN: i64 = -9223372036854775808; pub const INT_FAST8_MAX: u32 = 127; pub const INT_FAST16_MAX: u32 = 32767; pub const INT_FAST32_MAX: u32 = 2147483647; pub const INT_FAST64_MAX: u64 = 9223372036854775807; pub const UINT_FAST8_MAX: u32 = 255; pub const UINT_FAST16_MAX: u32 = 65535; pub const UINT_FAST32_MAX: u32 = 4294967295; pub const UINT_FAST64_MAX: i32 = -1; pub const INTPTR_MAX: u64 = 9223372036854775807; pub const INTPTR_MIN: i64 = -9223372036854775808; pub const UINTPTR_MAX: i32 = -1; pub const SIZE_MAX: i32 = -1; pub const RSIZE_MAX: i32 = -1; pub const WINT_MIN: i32 = -2147483648; pub const WINT_MAX: u32 = 2147483647; pub const SIG_ATOMIC_MIN: i32 = -2147483648; pub const SIG_ATOMIC_MAX: u32 = 2147483647; pub type int_least8_t = i8; pub type int_least16_t = i16; pub type int_least32_t = i32; pub type int_least64_t = i64; pub type uint_least8_t = u8; pub type uint_least16_t = u16; pub type uint_least32_t = u32; pub type uint_least64_t = u64; pub type int_fast8_t = i8; pub type int_fast16_t = i16; pub type int_fast32_t = i32; pub type int_fast64_t = i64; pub type uint_fast8_t = u8; pub type uint_fast16_t = u16; pub type uint_fast32_t = u32; pub type uint_fast64_t = u64; pub type __int8_t = ::std::os::raw::c_schar; pub type __uint8_t = ::std::os::raw::c_uchar; pub type __int16_t = ::std::os::raw::c_short; pub type __uint16_t = ::std::os::raw::c_ushort; pub type __int32_t = ::std::os::raw::c_int; pub type __uint32_t = ::std::os::raw::c_uint; pub type __int64_t = ::std::os::raw::c_longlong; pub type __uint64_t = ::std::os::raw::c_ulonglong; pub type __darwin_intptr_t = ::std::os::raw::c_long; pub type __darwin_natural_t = ::std::os::raw::c_uint; pub type __darwin_ct_rune_t = ::std::os::raw::c_int; #[repr(C)] #[derive(Copy, Clone)] pub union __mbstate_t { pub __mbstate8: [::std::os::raw::c_char; 128usize], pub _mbstateL: ::std::os::raw::c_longlong, } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { ["Size of __mbstate_t"][::std::mem::size_of::<__mbstate_t>() - 128usize]; ["Alignment of __mbstate_t"][::std::mem::align_of::<__mbstate_t>() - 8usize]; ["Offset of field: __mbstate_t::__mbstate8"] [::std::mem::offset_of!(__mbstate_t, __mbstate8) - 0usize]; ["Offset of field: __mbstate_t::_mbstateL"] [::std::mem::offset_of!(__mbstate_t, _mbstateL) - 0usize]; }; pub type __darwin_mbstate_t = __mbstate_t; pub type __darwin_ptrdiff_t = ::std::os::raw::c_long; pub type __darwin_size_t = ::std::os::raw::c_ulong; pub type __darwin_va_list = __builtin_va_list; pub type __darwin_wchar_t = ::std::os::raw::c_int; pub type __darwin_rune_t = __darwin_wchar_t; pub type __darwin_wint_t = ::std::os::raw::c_int; pub type __darwin_clock_t = ::std::os::raw::c_ulong; pub type __darwin_socklen_t = __uint32_t; pub type __darwin_ssize_t = ::std::os::raw::c_long; pub type __darwin_time_t = ::std::os::raw::c_long; pub type __darwin_blkcnt_t = __int64_t; pub type __darwin_blksize_t = __int32_t; pub type __darwin_dev_t = __int32_t; pub type __darwin_fsblkcnt_t = ::std::os::raw::c_uint; pub type __darwin_fsfilcnt_t = ::std::os::raw::c_uint; pub type __darwin_gid_t = __uint32_t; pub type __darwin_id_t = __uint32_t; pub type __darwin_ino64_t = __uint64_t; pub type __darwin_ino_t = __darwin_ino64_t; pub type __darwin_mach_port_name_t = __darwin_natural_t; pub type __darwin_mach_port_t = __darwin_mach_port_name_t; pub type __darwin_mode_t = __uint16_t; pub type __darwin_off_t = __int64_t; pub type __darwin_pid_t = __int32_t; pub type __darwin_sigset_t = __uint32_t; pub type __darwin_suseconds_t = __int32_t; pub type __darwin_uid_t = __uint32_t; pub type __darwin_useconds_t = __uint32_t; pub type __darwin_uuid_t = [::std::os::raw::c_uchar; 16usize]; pub type __darwin_uuid_string_t = [::std::os::raw::c_char; 37usize]; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct __darwin_pthread_handler_rec { pub __routine: ::std::option::Option<unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void)>, pub __arg: *mut ::std::os::raw::c_void, pub __next: *mut __darwin_pthread_handler_rec, } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { ["Size of __darwin_pthread_handler_rec"] [::std::mem::size_of::<__darwin_pthread_handler_rec>() - 24usize]; ["Alignment of __darwin_pthread_handler_rec"] [::std::mem::align_of::<__darwin_pthread_handler_rec>() - 8usize]; ["Offset of field: __darwin_pthread_handler_rec::__routine"] [::std::mem::offset_of!(__darwin_pthread_handler_rec, __routine) - 0usize]; ["Offset of field: __darwin_pthread_handler_rec::__arg"] [::std::mem::offset_of!(__darwin_pthread_handler_rec, __arg) - 8usize]; ["Offset of field: __darwin_pthread_handler_rec::__next"] [::std::mem::offset_of!(__darwin_pthread_handler_rec, __next) - 16usize]; }; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct _opaque_pthread_attr_t { pub __sig: ::std::os::raw::c_long, pub __opaque: [::std::os::raw::c_char; 56usize], } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { ["Size of _opaque_pthread_attr_t"][::std::mem::size_of::<_opaque_pthread_attr_t>() - 64usize]; ["Alignment of _opaque_pthread_attr_t"] [::std::mem::align_of::<_opaque_pthread_attr_t>() - 8usize]; ["Offset of field: _opaque_pthread_attr_t::__sig"] [::std::mem::offset_of!(_opaque_pthread_attr_t, __sig) - 0usize]; ["Offset of field: _opaque_pthread_attr_t::__opaque"] [::std::mem::offset_of!(_opaque_pthread_attr_t, __opaque) - 8usize]; }; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct _opaque_pthread_cond_t { pub __sig: ::std::os::raw::c_long, pub __opaque: [::std::os::raw::c_char; 40usize], } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { ["Size of _opaque_pthread_cond_t"][::std::mem::size_of::<_opaque_pthread_cond_t>() - 48usize]; ["Alignment of _opaque_pthread_cond_t"] [::std::mem::align_of::<_opaque_pthread_cond_t>() - 8usize]; ["Offset of field: _opaque_pthread_cond_t::__sig"] [::std::mem::offset_of!(_opaque_pthread_cond_t, __sig) - 0usize]; ["Offset of field: _opaque_pthread_cond_t::__opaque"] [::std::mem::offset_of!(_opaque_pthread_cond_t, __opaque) - 8usize]; }; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct _opaque_pthread_condattr_t { pub __sig: ::std::os::raw::c_long, pub __opaque: [::std::os::raw::c_char; 8usize], } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { ["Size of _opaque_pthread_condattr_t"] [::std::mem::size_of::<_opaque_pthread_condattr_t>() - 16usize]; ["Alignment of _opaque_pthread_condattr_t"] [::std::mem::align_of::<_opaque_pthread_condattr_t>() - 8usize]; ["Offset of field: _opaque_pthread_condattr_t::__sig"] [::std::mem::offset_of!(_opaque_pthread_condattr_t, __sig) - 0usize]; ["Offset of field: _opaque_pthread_condattr_t::__opaque"] [::std::mem::offset_of!(_opaque_pthread_condattr_t, __opaque) - 8usize]; }; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct _opaque_pthread_mutex_t { pub __sig: ::std::os::raw::c_long, pub __opaque: [::std::os::raw::c_char; 56usize], } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { ["Size of _opaque_pthread_mutex_t"][::std::mem::size_of::<_opaque_pthread_mutex_t>() - 64usize]; ["Alignment of _opaque_pthread_mutex_t"] [::std::mem::align_of::<_opaque_pthread_mutex_t>() - 8usize]; ["Offset of field: _opaque_pthread_mutex_t::__sig"] [::std::mem::offset_of!(_opaque_pthread_mutex_t, __sig) - 0usize]; ["Offset of field: _opaque_pthread_mutex_t::__opaque"] [::std::mem::offset_of!(_opaque_pthread_mutex_t, __opaque) - 8usize]; }; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct _opaque_pthread_mutexattr_t { pub __sig: ::std::os::raw::c_long, pub __opaque: [::std::os::raw::c_char; 8usize], } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { ["Size of _opaque_pthread_mutexattr_t"] [::std::mem::size_of::<_opaque_pthread_mutexattr_t>() - 16usize]; ["Alignment of _opaque_pthread_mutexattr_t"] [::std::mem::align_of::<_opaque_pthread_mutexattr_t>() - 8usize]; ["Offset of field: _opaque_pthread_mutexattr_t::__sig"] [::std::mem::offset_of!(_opaque_pthread_mutexattr_t, __sig) - 0usize]; ["Offset of field: _opaque_pthread_mutexattr_t::__opaque"] [::std::mem::offset_of!(_opaque_pthread_mutexattr_t, __opaque) - 8usize]; }; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct _opaque_pthread_once_t { pub __sig: ::std::os::raw::c_long, pub __opaque: [::std::os::raw::c_char; 8usize], } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { ["Size of _opaque_pthread_once_t"][::std::mem::size_of::<_opaque_pthread_once_t>() - 16usize]; ["Alignment of _opaque_pthread_once_t"] [::std::mem::align_of::<_opaque_pthread_once_t>() - 8usize]; ["Offset of field: _opaque_pthread_once_t::__sig"] [::std::mem::offset_of!(_opaque_pthread_once_t, __sig) - 0usize]; ["Offset of field: _opaque_pthread_once_t::__opaque"] [::std::mem::offset_of!(_opaque_pthread_once_t, __opaque) - 8usize]; }; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct _opaque_pthread_rwlock_t { pub __sig: ::std::os::raw::c_long, pub __opaque: [::std::os::raw::c_char; 192usize], } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { ["Size of _opaque_pthread_rwlock_t"] [::std::mem::size_of::<_opaque_pthread_rwlock_t>() - 200usize]; ["Alignment of _opaque_pthread_rwlock_t"] [::std::mem::align_of::<_opaque_pthread_rwlock_t>() - 8usize]; ["Offset of field: _opaque_pthread_rwlock_t::__sig"] [::std::mem::offset_of!(_opaque_pthread_rwlock_t, __sig) - 0usize]; ["Offset of field: _opaque_pthread_rwlock_t::__opaque"] [::std::mem::offset_of!(_opaque_pthread_rwlock_t, __opaque) - 8usize]; }; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct _opaque_pthread_rwlockattr_t { pub __sig: ::std::os::raw::c_long, pub __opaque: [::std::os::raw::c_char; 16usize], } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { ["Size of _opaque_pthread_rwlockattr_t"] [::std::mem::size_of::<_opaque_pthread_rwlockattr_t>() - 24usize]; ["Alignment of _opaque_pthread_rwlockattr_t"] [::std::mem::align_of::<_opaque_pthread_rwlockattr_t>() - 8usize]; ["Offset of field: _opaque_pthread_rwlockattr_t::__sig"] [::std::mem::offset_of!(_opaque_pthread_rwlockattr_t, __sig) - 0usize]; ["Offset of field: _opaque_pthread_rwlockattr_t::__opaque"] [::std::mem::offset_of!(_opaque_pthread_rwlockattr_t, __opaque) - 8usize]; }; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct _opaque_pthread_t { pub __sig: ::std::os::raw::c_long, pub __cleanup_stack: *mut __darwin_pthread_handler_rec, pub __opaque: [::std::os::raw::c_char; 8176usize], } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { ["Size of _opaque_pthread_t"][::std::mem::size_of::<_opaque_pthread_t>() - 8192usize]; ["Alignment of _opaque_pthread_t"][::std::mem::align_of::<_opaque_pthread_t>() - 8usize]; ["Offset of field: _opaque_pthread_t::__sig"] [::std::mem::offset_of!(_opaque_pthread_t, __sig) - 0usize]; ["Offset of field: _opaque_pthread_t::__cleanup_stack"] [::std::mem::offset_of!(_opaque_pthread_t, __cleanup_stack) - 8usize]; ["Offset of field: _opaque_pthread_t::__opaque"] [::std::mem::offset_of!(_opaque_pthread_t, __opaque) - 16usize]; }; pub type __darwin_pthread_attr_t = _opaque_pthread_attr_t; pub type __darwin_pthread_cond_t = _opaque_pthread_cond_t; pub type __darwin_pthread_condattr_t = _opaque_pthread_condattr_t; pub type __darwin_pthread_key_t = ::std::os::raw::c_ulong; pub type __darwin_pthread_mutex_t = _opaque_pthread_mutex_t; pub type __darwin_pthread_mutexattr_t = _opaque_pthread_mutexattr_t; pub type __darwin_pthread_once_t = _opaque_pthread_once_t; pub type __darwin_pthread_rwlock_t = _opaque_pthread_rwlock_t; pub type __darwin_pthread_rwlockattr_t = _opaque_pthread_rwlockattr_t; pub type __darwin_pthread_t = *mut _opaque_pthread_t; pub type intmax_t = ::std::os::raw::c_long; pub type uintmax_t = ::std::os::raw::c_ulong; extern "C" { pub fn double_byte(n: i8) -> i8; } pub type __builtin_va_list = *mut ::std::os::raw::c_char; }
Oh boy. Now we have a Rust definition of every single type exported by stdint on your platform, sitting inside your auto-generated blob. You're compiling it (and discarding most of it) every time. Down at the bottom, you have the part you actually care about:
#![allow(unused)] fn main() { extern "C" { pub fn double_byte(n: i8) -> i8; } }
It's not doing any real harm, other than making your IDE and compiler work harder (it's also one reason I often write the imports by hand for simpler setups). But what if you DON'T want to generate the most amazingly large imports (for fun, try bindgen on some Linux headers...). If this really bothers you, the easiest approach is to adopt an explicit list of what you want to import:
In your build.rs:
#![allow(unused)] fn main() { let bindings = bindgen::Builder::default() .header("src/clib.h") .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .allowlist_function("double_byte") .generate() .expect("Unable to generate bindings"); }
Bindgen has an amazing number of options. If you want to, you can make a list of functions in a file and call
allowlist_file. The downside is that now you have to list what you want to import. For a big library, that may be painful.