cbindgen icon indicating copy to clipboard operation
cbindgen copied to clipboard

Rust Option to C

Open pmirabel opened this issue 6 years ago • 4 comments

(working solution at the end of this post)

Hello, I managed to get an enum to represent an option and port it to C side, but I realised something that I can't understand, maybe you can help me ?

First I tried :

//LIB.RS
#[repr(C)]                //Edit: also tried repr(u16)
#[derive(Debug, Clone, Copy, PartialEq)]
// Cbindgen tweak to represent an option
pub enum OptionU16 {
    Some(u16),
    None
}

Which generate :

///LIB.HPP
 struct OptionU16 {
  enum class Tag {
    Some,
    None,
  };

  struct Some_Body {
    Some _0;
  };

  Tag tag;
  union {
    Some_Body some;
  };
};

At first attempt, everything was okay :

//MAIN.CPP
OptionU16 w;
w.tag=OptionU16::Tag::Some;
w.some._0=10;

But after that, I tried to get the enum variant from rust side :

// LIB.RS 
#[no_mangle] 
pub extern "C" fn test_get_some_10() -> OptionU16 {
    OptionU16::Some(42)
}
// MAIN.CPP
OptionU16 foo = test_get_some_10();

At this point, foo.tag = foo.some._0 = 32767 (Max value for a U16) Can someone explain to me why ?

If someone need a working solution, I found this to be okay :

// LIB.RS
#[repr(C)]
pub struct OptU16 {
    tag: OptU16Tag,
    payload: OptU16Payload,
}

#[repr(C)]
pub enum OptU16Tag { SOME, NONE }

#[repr(C)]
pub union OptU16Payload {
   SOME: u16,
   NONE:()
}

pmirabel avatar Aug 28 '19 07:08 pmirabel

Hmm, weird... Is some of the transformations here not valid for some alignment / not what https://github.com/rust-lang/rfcs/blob/master/text/2195-really-tagged-unions.md specifies?

Or is it a rustc bug? I'd have to check carefully.

cc @Gankra

emilio avatar Sep 20 '19 15:09 emilio

I'm having trouble reproducing this. Can you provide a standalone project I can build, and/or specify what platform you're targeting?

Also interestingly in the bare repr(C) case our codegen is probably slightly incorrect. repr(C) is "C enum sized", which is platform/implementation-defined thing that Rust has to vaguely guess at (usually int iirc). But a C++ enum class is guaranteed to be int.

Gankra avatar Oct 04 '19 15:10 Gankra

Hi, Sorry for my very late answer... I'm under MacOS right now, but this code should run on a x64 upboard + debian
Please see this repo for a test program.

pmirabel avatar Mar 25 '20 10:03 pmirabel

I had a similar problem for C structs defined in a shared C library and allocated as an option in Rust: Option<MyStructFromFFI> which is leading to undefined (non-C) memory alignment for MyStructFromFFI if used by the shared C libary. I got it working by using std::mem::MaybeUninit instead of std::option::Option.

Note: I was using bindgen instead of cbindgen, but the problem seems similar.

quambene avatar Aug 02 '22 20:08 quambene