Recently, I’ve considered the limitations of C FFI interfaces as I work on a project which is trying to use a Rust library with Swift code.
The goal is to save writing FFI code with an individual method per struct field and allow typed data transfer between two different systems.
After experimenting with various interface designs, I am debating to use Protocol Buffers or similar technology to serialize data across the FFI boundary. While the serialization process may not be entirely performant, it may meet my needs. I was actually considering exploring the use of Cap’N Proto, but unfortunately, there are no complete Swift implementations yet.
The idea is if owned data must be transferred across the FFI boundary, then one
side will serialize the data via a defined protocol buffer type into a simple
byte array. Then the bytes will be transferred to the other side. More
concretely, if a Rust library was returning a post
struct with various
fields like title and body, the post
type would be serialized into a byte array
using a Rust protocol buffer library. Then a Swift protocol buffer library
would deserialize the bytes into a rich Swift struct using the same protocol
buffer type definition.
It is hardly the first usage of protocol buffers or similar technology for this purpose, but it is unusual inside the same application binary. Usually, the boundary is between two different applications possibly communicating via the network or data on storage.
The advantage of using protocol buffers is that code can be generated for most languages using the same type definitions. While protocol buffer types can evolve, in this case, all systems in the same application will always use the latest definitions.
The major disadvantage is the runtime performance penalty when serializing and deserializing the data.
Perhaps this idea will work out, but I wonder if there are any other good solutions.