Interface generation with Rust macros

Let’s say I have some Rust function which I want to expose through FFI. There are lots of possibilities depending on the environment and the language I want to interface to.

For example, string arguments may be pascal-lengthed, separately-lengthed or null-terminated. Characters may be ASCII, UTF8/16/32. The calling convention may be stdcall or cdecl. The size of the ints and bools are context dependent. The exported name may be camel case or snake case, and may need an initial underscore.

I may want to expose just one of these interface types, depending on compile skips, or I may want to expose more than one in the same dynamic library.

This sounds like a job for Rust macros. Some questions, then:

  • Is this something that Rust macros could do?
  • Does these technology already exist on crates.io?
  • If I start a project to write something like this, would anybody be interested in joining in, or giving some more use cases to drive the design?

cbindgen does this automatically.

For string encoding only utf-8 makes sense. Where it seems anything else would be possible, it’s just a painful detour back to utf-8.

APIs that take explicit length of string in a separate argument (not Pascal string) can be shared at zero cost. Otherwise you’ll need CStr/CString.

I guess I should come clean about my actual use-case here. I am writing Excel addins in Rust. Excel requires strings that are UTF16 and preceded by their length, like Pascal strings. Thus I don’t have a choice about string encoding. The same is likely to be true for other applications.

I have used cbindgen for generating C header files from Rust functions, but I have first ensured that the Rust functions are defined with C-friendly types from FFI, and avoiding structs and tuples. Otherwise you’d end up with an interface that was pretty hard to use from languages like Python.

My idea here is to use macros to generate thunk functions that wrap the hand-written Rust methods, converting &str into friendlier types using CStr/CString etc.

To partly answer my own question, it is possible to use Rust macros to generate an interface in this way, though only using the nightly build of the Rust compiler. A good example of someone doing just this is https://github.com/PyO3

This produces a python-friendly interface from rust functions and structs. I think it would be possible to use the same techniques to produce interfaces that were friendly to other language environments, such as Microsoft environments using pascal-lengthed UTF16 strings.

I shall experiment, and if I get anywhere I shall post back to this thread.