Android/ios rust ffi from a single rust lib

I am experimenting with Kotlin (android) and Swift (Apple) with rust ffi.

In case of kotlin the function name goes like this: Java_com_user_projectname_MainActivity_functionname, the function arguments and outputs are jni specific like jstring whereas in case of Swift the function name can be referred to normally and the output is normal c_char.

I don't want to write duplicate code and preferably I want to avoid macros. Is there any elegant solution where a single function body can be referred to by different names and signatures?

I am thinking of creating a common library and import the functions from there into two separate libraries, but is it possible to have a generic function signature in the common library with zero overhead of converting between datatypes?

I don't want to write duplicate code and preferably I want to avoid macros. Is there any elegant solution where a single function body can be referred to by different names and signatures?

No, because a jstring is not a *const c_char is not a &str. You will need a conversion at some point.
A library implementing the core functionality and then 2 wrappers exposing the parts for either Kotlin/Java or Swift/C consumption is the way to go.

But don't fret, that doesn't mean you need to write those wrappers all by yourself.
We did that before! -> UniFFI is our way to build cross-platform libraries that can be used from Kotlin or Swift (or Python or Ruby or ...).
It lets you define your API once and then generates the necessary bindings on both sides. You write pure Rust code and some additional code in your target language to call the generated parts. Conversion of data types is done behind the scene for you.

3 Likes

I believe in your solution one has to create the bindings first and then compile the app separately. That works too. I would have preferred a single step process though.

I was thinking more along the lines of preparing the functions before compile phase (using build.rs or proc macro) so that generation of bindings is done automatically using a single command. But your solution may save time since the bindings need not be prepared everytime while building the app although rust's incremental compilation should ideally take care of it.