Description
#[inline(never)]
extern "C" fn foo(x: ~int) -> Option<~int> { Some(x) }
pub fn main() {
unsafe {
let f: extern "C" fn(~int) -> ~int = ::std::cast::transmute(foo);
assert_eq!(*f(~0xDEADBEE), 0xDEADBEE);
}
}
Works on x86_64; crashes on i686. The Rust struct
or enum
gets an LLVM struct wrapped around it, which changes the ABI on 32-bit x86 if the type is used as a return type. This is an unpleasant surprise for projects using the FFI to represent nullable pointers from C, and there isn't an immediately obvious workaround.
But also:
struct AlmostInt(int);
#[inline(never)]
extern "C" fn foo(x: int) -> AlmostInt { AlmostInt(x) }
pub fn main() {
unsafe {
let f: extern "C" fn(int) -> int = ::std::cast::transmute(foo);
assert_eq!(f(0xDEADBEE), 0xDEADBEE);
}
}
Same thing. But we probably don't want one-field structs to deviate from the ABI for the equivalent C struct, and the newtype case can (I think?) just use a wrapper to do the conversion instead. This yields the slightly counterintuitive result that adjoining a value to a type makes its representation slightly more efficient (option returned in register vs. newtype returned on stack), but we might be willing to live with that.