Rust on Android

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++.

(We wrote a similar article for iOS)

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 ~/Library/Android/sdk.

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.

Hello World

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 #[no_mangle]).

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 jni and ffi libraries make this fairly safe though and later on we will link to some patterns we use in Shard 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.

Android Studio

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 rust-android-example.

Open up 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 System.loadLibrary.

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.

Next steps

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. Take a look at some of the patterns we have adopted when building Shard. All the code is available on GitHub.

The final code for this tutorial and a great starting point if you plan on using rust on Android can be found on GitHub. If you have any questions or comments feel free to tweet at me.