randomPoison / cs-bindgen

Experiment in using Rust to build a library that can be loaded by Unity across multiple platforms
4 stars 0 forks source link

Unify FromAbi and IntoAbi traits #35

Closed randomPoison closed 4 years ago

randomPoison commented 4 years ago

The current setup for converting Rust types to-and-from an FFI-safe representation uses a pair of traits FromAbi and IntoAbi. It would simplify various parts of the code generation logic if we could unify these traits into a single Abi trait. However, there currently a couple of places where a type handles being sent to C# vs when it's received from C#. These are discussed in the sections below.

I'm slightly hesitant to unify these two fully, as I'm not yet 100% certain that all Rust types that we want to be able to marshal to C# can be uniformly represented when sending vs receiving. However, there are pretty big advantages to unifying them, so I think it's worth the potential risk of running into edge cases that don't work in the future.

String

Right now, String is converted into a RawVec<u8> when passed to C#, but is received as a RawSlice<u16> when received from C#. This difference is there for two reason:

To fix these, can can export a helper function that takes a RawSlice<u16> and returns a RawVec<u8>. When passing a string to Rust code, the C# code can call the helper function to convert its string to a Rust-compatible representation while still avoiding the extra string copy.

Zero-Sized Types

Zero-sized types aren't FFI safe, and therefore don't generally implement FromAbi or IntoAbi. However, we currently make an exception for (), which implements IntoAbi. This is to uniformly handle exported Rust functions that don't return a value. However, this approach isn't generalized to all ZSTs, and is potentially unsound if () is used as a field in a struct that is returned from a Rust function.

To make it generally possible to pass ZSTs to-and-from C#, we can instead represent the types as a single byte in FFI. While this is slightly less efficient, it ensures that the generated code will always be FFI-safe. Exported functions that don't return a value should still generate a void function in C# and the raw bindings, however functions that explicitly return () will likely have to be generated to return a dummy single-byte type.