NonZeroU128 through FFI


I need to manipulate a Rust ID type from C, which looks like this:

pub struct RustId(pub NonZeroU128);

Since u128 is not FFI-safe, and since I'd like to avoid allocating memory everywhere if possible, I've created these helper structs and functions:

pub struct c_id {
    _data: [u8; 16],

pub struct opt_c_id {
    pub has_value: bool,
    pub value: c_id,

pub extern "C" fn c_id_new(value: u64) -> opt_c_id {
    match NonZeroU128::new(value as u128) {
        Some(value) => opt_c_id {
            has_value: true,
            value: unsafe { mem::transmute::<NonZeroU128, c_id>(value) },
        None => opt_c_id {
            has_value: false,
            value: c_id { _data: [0; 16] },

pub extern "C" fn c_id_display(id: c_id) {
    // Probably check to see if the array is full of zeroes here.
    let id = unsafe { mem::transmute::<c_id, RustId>(id) };
    println!("{}", id.0);

I can then make use of this API like so:

#include "id.h"

int main() {
    opt_c_id opt_id = c_id_new(42);
    if (opt_id.has_value) {
        c_id id = opt_id.value;
    return 0;

But is it actually safe? Is this approach valid or is there another way? I will have a lot of these types around, so allocating memory each time to then cast to *mut c_void seems bad to me.

For full reference, here is the header file as generated by cbindgen:

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

typedef struct c_id {
  uint8_t _data[16];
} c_id;

typedef struct opt_c_id {
  bool has_value;
  struct c_id value;
} opt_c_id;

struct opt_c_id c_id_new(uint64_t value);

void c_id_display(struct c_id id);

Thanks in advance for your replies.

You can use u128::to_ne_bytes() and u128::from_ne_bytes() to safely convert between a u128 and a [u8; 16] array. Otherwise, your approach looks entirely sound.


Indeed these two functions can be of great help here. Thanks for checking the soundness of the code.