Creating Rust Module for Multiplatform Application

Fajar Dimar Habibi
7 min readJul 24, 2023

--

Rust is a modern programming language that has been gaining popularity due to its performance, safety, and reliability. One of the advantages of Rust is its ability to create multiplatform applications. In this article, we will discuss the steps to create a Rust multiplatform application step by step.

Multiplatform Application using RUST

Step 1: Install Rust and Cargo

The first step in creating a Rust multiplatform application is to install Rust on your computer. You can download Rust from the official website (https://www.rust-lang.org/tools/install).

Step 2: Set up Your Rust Environment

To create a Rust multiplatform application, you need to set up your Rust environment. You can do this by running the following command in your terminal:

rustup target add {target}

Replace {target} with the target platform you want to support. For example, if you want to create an application for Windows, you can run the following command:

a. Mobile Platform (Android & iOS)

Make sure for iOS platform cargo-lipo and cbindgen is installed. It will be used to produce C headers and IOS builds, respectively. You can use this link to access the original article

cargo install cbindgen
cargo install cargo-lipo

Add the target platform for iOS and Android

//iOS
rustup target add aarch64-apple-ios
rustup target add x86_64-apple-ios

//Android
rustup target add armv7-linux-androideabi
rustup target add i686-linux-android
rustup target add aarch64-linux-android
rustup target add x86_64-linux-android
rustup target add x86_64-unknown-linux-gnu
rustup target add x86_64-apple-darwin
rustup target add x86_64-pc-windows-gnu
rustup target add x86_64-pc-windows-msvc

b. Web Browser Platform (WebAssembly)

To running on the browser we need to install dependencies in this link

curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
cargo install cargo-generate
//install npm package manager for javascript family
npm install npm@latest -g

Add the target platform for browser with WebAssembly

rustup target add wasm32-unknown-unknown

c. Desktop Platform (MacOs, Linux and Windows)

We will create Desktop Platform by using Tauri Framework. You can refer installation to origin source to this link.

cargo install create-tauri-app --locked

d. Server Platform

You can add much Rust target platform that can be ran into different distribution such as unix base and windows.

Step 3: Create Your Rust Library Project

Once you have set up your Rust environment, you can create your Rust library project. You can do this by running the following command in your terminal:

cargo new {project_name}

Replace {project_name} with the name of your project. You can follow this code :

cargo new hello --lib

Step 4: Create Code Rust Module

a. Core Module

To create core module’s code, we’ll add a new module hello to the hello library and use greetings_from_rust() method to get our text.

// rust-crossplatform-library/hello/src/lib.rs
pub mod hello {
pub fn greetings_from_rust() -> String {
String::from("Hello from rust")
}
}

Don’t forget to include core library code to each platform module in Cargo Package

b. iOS Module

create iOS module with following code:

cargo new helloios --lib
# helloios/Cargo.toml
[package]
name = "helloios"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
hello = { path = "../hello" }

[lib]
name = "helloios"
crate-type = ["staticlib", "cdylib"]
// helloios/src/lib.rs
use std::os::raw::{c_char};
use std::ffi::{CString, CStr};

use hello::hello;

#[no_mangle]
pub extern fn rust_hello(to: *const c_char) -> *mut c_char {
let c_str = unsafe { CStr::from_ptr(to) };
let recipient = match c_str.to_str() {
Err(_) => "there",
Ok(string) => string,
};
let str = hello::greetings_from_rust();
CString::new(str.to_owned() + recipient).unwrap().into_raw()
}

#[no_mangle]
pub extern fn rust_hello_free(s: *mut c_char) {
unsafe {
if s.is_null() { return }
CString::from_raw(s)
};
}

c. Android Module

create following code to create Android Module

cargo new helloandroid --lib
# helloandroid/Cargo.toml
[package]
name = "helloandroid"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
jni = "0.5.2"
hello = { path = "../hello" }

[lib]
name = "helloandroid"
crate-type = ["staticlib", "cdylib"]
// rust-crossplatform-library/helloandroid/src/lib.rs
extern crate jni;

use std::os::raw::{c_char};
use std::ffi::{CString};

use jni::JNIEnv;
use jni::objects::{JClass, JObject, JValue};

use hello::hello;

pub type Callback = unsafe extern "C" fn(*const c_char) -> ();

#[no_mangle]
#[allow(non_snake_case)]
pub extern "C" fn invokeCallbackViaJNA(callback: Callback) {
let s = CString::new(hello::greetings_from_rust()).unwrap();
unsafe { callback(s.as_ptr()); }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern "C" fn Java_com_rc_rustspike_myapplication_MainActivity_invokeCallbackViaJNI(
env: JNIEnv,
_class: JClass,
callback: JObject
) {
let s = String::from(hello::greetings_from_rust());
let response = env.new_string(&s)
.expect("Couldn't create java string!");
env.call_method(callback, "callback", "(Ljava/lang/String;)V",
&[JValue::from(JObject::from(response))]).unwrap();
}

d. Web Browser Module

Create the following code to create Web module:

cargo generate --git https://github.com/rustwasm/wasm-pack-template
//named the project with helloweb
# helloweb/Cargo.toml
[package]
name = "helloweb"
version = "0.1.0"
authors = ["Fajar Dimar Habibi <fajar.dimar@ngecamp.co.id>"]
edition = "2018"

[lib]
crate-type = ["cdylib", "rlib"]

[features]
default = ["console_error_panic_hook"]

[dependencies]
wasm-bindgen = "0.2.63"
hello = { path = "../hello" }

# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.6", optional = true }

# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. It is slower than the default
# allocator, however.
wee_alloc = { version = "0.4.5", optional = true }

[dev-dependencies]
wasm-bindgen-test = "0.3.13"

[profile.release]
# Tell `rustc` to optimize for small code size.
opt-level = "s"
// rust-crossplatform-library/helloweb/src/lib.rs
mod utils;

use hello::hello;
use wasm_bindgen::prelude::*;

// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

#[wasm_bindgen]
extern "C" {
fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet() {
let str = String::from(hello::greetings_from_rust());
alert(&str);
}

#[wasm_bindgen]
pub fn greeting_string() -> String {
let hello = String::from(hello::greetings_from_rust());
return hello.into();
}

e. Desktop Module

Create following code to create Desktop Module:

cargo create-tauri-app
// and name the project with desktop
// rust-crossplatform-library/desktop/src/src-tauri/cargo.toml
[package]
name = "desktop"
version = "0.0.0"
description = "A Tauri App"
authors = ["Dimar"]
license = ""
repository = ""
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[build-dependencies]
tauri-build = { version = "1.2", features = [] }

[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.2", features = ["shell-open"] }
hello = { path = "../../hello" }

[features]
# by default Tauri runs in production mode
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
default = ["custom-protocol"]
# this feature is used used for production builds where `devPath` points to the filesystem
# DO NOT remove this
custom-protocol = ["tauri/custom-protocol"]
// rust-crossplatform-library/desktop/src/src-tauri/src/main.rs
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
use hello::hello;
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
#[tauri::command]
fn greet(name: &str) -> String {
format!("{} {name}", hello::greetings_from_rust())
}

fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

f. Server Module

# rust-crossplatform-library/desktop/cargo.toml
[package]
name = "server"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
hello = { path = "../hello" }

Step 5: Create Application with Rust Module

We will dive into the application to create multiplatform apps. The following code structure will look like this.

Tree Code Structure

All those following code you can copy from my github project and run all those the project. Android and iOS project you can refer to this link and follow the instruction. For browser, desktop and server application are below

a. Web Browser

// rust-crossplatform-library/helloweb
wasm-pack build

after the pkg package has been created, add the following code to package.json

// rust-crossplatform-library/web/package.json
"devDependencies": {
...,
...,
"hello": "file:../helloweb/pkg"
}

run the browser application and open http://localhost:8080/

// rust-crossplatform-library/web
npm start
WebAssembly Browser Application

b. Desktop Application

Go to the folder desktop application run the following code

// rust-crossplatform-library/desktop
cargo tauri dev
Desktop Application

c. Server Application

Go to the folder server application run the following code

// rust-crossplatform-library/server
cargo run
Server Application

Conclusion

Creating a Rust multiplatform application is easy with the right tools and knowledge. By following the steps outlined in this article, you can create a Rust application that can run on multiple platforms.

Reference

--

--

Fajar Dimar Habibi
Fajar Dimar Habibi

Written by Fajar Dimar Habibi

Software Engineer, Entrepreneur's Journey, Game Changer, Long Life Learner | profile: https://whois.dimar.co.id/

No responses yet