let lots: fun = rust + unity;

In a hurry

  1. Get the FunWithRustAndUnity repo.
  2. Return here if hints are needed.


Unity came into play for an existing pet project mainly due to it’s cross-platform support. While C# is powerful and (kind of) fun, it is also for good or bad… traditional. Approaching Rust for the core application engine and leveraging Unity (and C#) for the user interface and scene management presented an interesting alternative that would bring back the fun to a project that had been on hold for too long.


The goal is to target iOS (arm64), Android (arm32 and arm64, x86 and x86_64 may come later), WebGL and also macOS (arm64 and x86_64 on a fat library), the later because it is our development platform. The future will probably also bring Windows and Linux, but that will come after the first release on the initial platforms.


I won’t go into explaining the basics. I’ll assume that you have some level of familiarity with Rust and Unity, including deployment of Unity projects on multiple platforms, so I’ll keep to the strictly relevant for the Rust+Unity integration. Bear in mind that everything was setup using macOS as development platform, tweaks will be necessary if using other platforms. Also, I’ll just describe the manual process, no build automation process is used.

+ RustFun (Rust workspace)
+ fun (library to be used as Unity plug-in)
+ funcli (binary cli to use and interact with the library)
+ UnityFun (Unity 2D URP basic project)
+ Assets
+ Plugins
+ Android
+ aarch64
+ armv7
+ Editor
+ iOS
+ macOS
+ WebGL

Into Rust

The no_mangle annotation is required, for every function you want to export from Rust to Unity. This gets us going with predictable names when importing into C#. Be aware that when using no_mangle, the Rust mechanism that ensures that we don’t get names clashing between functions in distinct libraries or even from the system, is disabled for these functions (for more information, check this Rust RFC). The extern "C" modifier is necessary to specify the ABI that we want to use (C# happily uses the C ABI).

Excerpt of FunWithRustAndUnity/RustFun/src/lib.rs

Build targets

In order to be able to build the libraries for multiple targets which will be used as Unity plug-ins, we’ll need to add support for each of those targets.

rustup target add wasm32-unknown-emscripten # WebGL with EMScripten
rustup target add aarch64-linux-android # Android on ARM 64 bits
rustup target add armv7-linux-androideabi # Android on ARM 32 bits
rustup target add aarch64-apple-darwin # macOS on Apple Silicon
rustup target add x86_64-apple-darwin # macOS on Intel
rustup target add aarch64-apple-ios # iOS

Crate type

We’ll also need to ensure that the library crate type is configured with the output library types that we’ll be using for each target.

Building for macOS (single architecture — Unity Editor)

If you’re in the development phase of your application, then building for macOS and specifically for the processor architecture of your development machine is enough. So…

Excerpt of RustFun/fun/Cargo.toml:[lib]
crate-type = ["cdylib", "rlib"]
host_triplet=$(rustc -vV | sed -n 's|host: ||p')
cargo build --target ${host_triplet} --package fun

Building for macOS (fat library)

For this we need to first build both macOS architectures:

Excerpt of RustFun/fun/Cargo.toml:[lib]
crate-type = ["cdylib"]
cargo build --target aarch64-apple-darwin --package fun
cargo build --target x86_64-apple-darwin --package fun
lipo -create -output target/fun.bundle \
target/aarch64-apple-darwin/debug/libfun.dylib \

Building for iOS

iOS is in fact, pretty straightforward.

Excerpt of RustFun/fun/Cargo.toml:[lib]
crate-type = ["staticlib"]
cargo build --target aarch64-apple-ios --package fun

Building for Android

Android is a bit trickier because we do need to use the linker from Android NDK. Fortunately, given that we are using Unity, we can simplify this by using the one bundled with Unity (ensure that you have the corresponding modules installed).

Excerpt of RustFun/fun/Cargo.toml:[lib]
crate-type = ["cdylib"]
# Android ARMV7
export CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER="$(ls /Applications/Unity/Hub/Editor/*/PlaybackEngines/AndroidPlayer/NDK/toolchains/llvm/prebuilt/*/bin/armv7a-linux-androideabi*-clang | sort | tail -n 1)"
cargo build --target armv7-linux-androideabi --package fun
# Android ARM64
export CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="$(ls /Applications/Unity/Hub/Editor/*/PlaybackEngines/AndroidPlayer/NDK/toolchains/llvm/prebuilt/*/bin/aarch64-linux-android*-clang | sort | tail -n 1)"
cargo build --target aarch64-linux-android --package fun

Building for WebGL

With WebGL we are back to needing a linker, and again Unity to the rescue (ensure that you have the corresponding modules installed).

Excerpt of RustFun/fun/Cargo.toml:[lib]
crate-type = ["staticlib"]
export CARGO_TARGET_WASM32_UNKNOWN_EMSCRIPTEN_LINKER="$(ls /Applications/Unity/Hub/Editor/*/PlaybackEngines/WebGLSupport/BuildTools/Emscripten/llvm/clang | sort | tail -n 1)"
cargo build --target wasm32-unknown-emscripten --package fun


After copying all the plug-ins into the respective folders, just right-click the Plugins folder on Unity and Refresh. All the plugins should become visible. Click on the macOS/fun.bundle and on the Unity Inspector, unselect Editor (remember that we have a specific library for the Editor).


Addenda: Building with Rust nightly (crate-type)

If using Rust nightly the quick and dry commands to build (instead of cargo) would be:



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store