You may have heard of rust, it’s a systems programming language designed for memory safety and speed. Built by Mozilla to power the next generation high performance cross platform software. If you haven’t already I suggest having a look at the great learning material, but keep in mind that can take a while to get into and appreciate so I suggest writing more than just a hello world.
If you’re an Android developer you may be asking yourself how and why you would make use of rust on Android. This article will mostly cover the how. As to why, the most compelling reason for us at Visly is that it enables us to share code between Android and iOS in a performant and safe manner, in a language much easier to work with than C++.
Getting set up
Before we get started we need to make sure we have the rust toolchain set up. We will assume you already have a working Android toolchain, if not you should download Android Studio and set it up according to any other Android guide. One thing to make sure is that you have set up your
$ANDROID_HOME environment variable. With a typical install on macOS this should be set to
Next we have to make sure rust is installed on our system. Rustup makes this a simple one-liner.
You can validate rust was successfully installed and located in your
PATH by running
rustc --version. Once rust is installed on your system we need to make rust aware of how to build for the supported Android architectures. Rust can build binaries for all sorts of architectures but not by default. To add the appropriate architectures run the following command.
Next we have to set up some standalone toolchains to build rust for Android’s various supported architectures. These only need to be installed once for your system and not multiple times for each project, so instead of installing them to the project folder we will install them in our home directory.
Finally we have to tell rust about these toolchains. Append the following to
~/.cargo/config, creating the file if it doesn’t already exist.
Let’s build a small hello world app using rust! We’ll start by creating our rust library and later move on to creating our Android Studio project.
This will create a basic rust library managed by
cargo, the rust equivalent to gradle, which we will later make use of in our Android Studio project. The
--lib flag instructs
cargo that we want to create a library, not an executable binary. Within the newly created project folder you will find
Cargo.toml which much like a
build.gradle file defines metadata for your library as well as any dependencies. You’ll also find a
srcfolder which contains our rust source code. The
src folder only contains
lib.rs which itself only contains a sample test function. We can start by removing everything in this file and replacing it with the following.
We start off with telling rust that this file will only be used when targeting Android with
#[cfg(target_os=”android”)] and because JNI required CamelCase function names which are not standard on Rust we also allow that with
#[allow(non_snake_case)]. There are a couple other things to notice here, because we are interfacing with Kotlin we have to make use of C calling conventions and JNI, this means we have to tell rust not to mangle any names (with
We then define a basic function which constructs a new string given an input string. A lot is happening there as we need to transform the string from a jni string to a C string to a rust string and back. The rust
ffi libraries make this fairly safe though and later on we will link to some patterns we use in Visly to make this easier. In a larger application this is not much of an issue as the glue code between Kotlin and Rust can be kept fairly small.
We also need to update our
Cargo.toml to add a dependency on the
jni library as well as define the name of the final binary and how to compile it.
Now the last thing to do before we are ready to move onto our Android Studio project is build our library for our supported targets.
Time to start a new Android Studio project and test this out in a simulator. Start by going through the standard project setup, we’ll be using Kotlin but you can use Java if you want as well. We will name the project
androidsaving it next to our rust library at the root of
MainActivity.kt and paste the following code. We declare an external function
hello which tells Android to look for a native library function named
Java_example_com_android_MainActivity_hello. Before we can call this function we have to load our library using
At this point the app should compile however it will crash as soon as we run it. That’s because we haven’t yet included the native library in our project. Let’s copy it in.
Now we can re-build and re-run our app and we should see “Hello World” written out in Logcat. Congrats! You have manage to compile and run Rust code on Android.
Automating the process
Automating this process of copying over binaries is actually pretty easy with a simple bash script.
Now save this as
rust-android-example/install.sh and just run the script after any updates to your rust code to compile and install it into your Android Studio project. If you want to get fancy you can add this as a build step in your gradle file so it is automatically run any time you build your Android Studio project.
While the code above works, it isn’t super easy to work with. In a larger project we want a way to encapsulate the ugly bits of interacting with Rust from Kotlin and vice versa. In a later post I’ll cover the patterns we adopted when building Visly to simplify this.