Web Development

WebAssembly dengan Rust

Jalankan kode Rust di browser menggunakan WebAssembly β€” pelajari wasm-bindgen, wasm-pack, memory model, optimasi performa, dan JavaScript interop untuk membangun fitur web berperforma tinggi.

1. Pengenalan WebAssembly & Rust

WebAssembly (Wasm) adalah format instruksi biner yang berjalan di browser dengan kecepatan mendekati native. Wasm bukan pengganti JavaScript β€” melainkan pelengkap untuk tugas-tugas berat yang memerlukan performa tinggi: image processing, cryptography, game engines, video encoding, dan simulasi ilmiah.

Rust adalah bahasa pemrograman yang menjadi pilihan utama untuk mengkompilasi kode ke WebAssembly. Rust menawarkan memory safety tanpa garbage collector, zero-cost abstractions, dan tooling yang luar biasa untuk WebAssembly.

Mengapa Rust + WebAssembly?

KeunggulanPenjelasanDampak
PerformaWasm 10-100x lebih cepat dari JS untuk komputasiReal-time processing
Memory SafetyRust tanpa garbage collector = predictable performanceTidak ada GC pauses
Bundle SizeRust menghasilkan .wasm yang kecilFast download
Ecosystemwasm-pack, wasm-bindgen, web-sysDX yang baik
ReusabilityKode Rust yang sama bisa jalan di server dan browserCode sharing

Arsitektur WebAssembly

Diagram: Rust β†’ Wasm Pipeline
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚            RUST β†’ WEBASSEMBLY PIPELINE                 β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                        β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ Rust     β”‚    β”‚ wasm-pack β”‚    β”‚ .wasm binary   β”‚  β”‚
β”‚  β”‚ Source   │───►│ / cargo   │───►│ + JS glue      β”‚  β”‚
β”‚  β”‚ (.rs)    β”‚    β”‚ build     β”‚    β”‚ code           β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                           β”‚           β”‚
β”‚                                    β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”   β”‚
β”‚                                    β”‚  Browser     β”‚   β”‚
β”‚                                    β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”‚   β”‚
β”‚                                    β”‚  β”‚ Wasm   β”‚ β”‚   β”‚
β”‚                                    β”‚  β”‚ VM     β”‚ β”‚   β”‚
β”‚                                    β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚   β”‚
β”‚                                    β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”‚   β”‚
β”‚                                    β”‚  β”‚ JS     β”‚ β”‚   β”‚
β”‚                                    β”‚  β”‚ Engine β”‚ β”‚   β”‚
β”‚                                    β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚   β”‚
β”‚                                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                        β”‚
β”‚  Interop: JS ←→ Wasm melalui wasm-bindgen glue code   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

2. Environment Setup

Shell β€” Install Tools
# 1. Install Rust (jika belum)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env

# 2. Tambahkan WebAssembly target
rustup target add wasm32-unknown-unknown

# 3. Install wasm-pack (build tool untuk Rust β†’ Wasm)
cargo install wasm-pack

# 4. Install cargo-generate (untuk scaffolding)
cargo install cargo-generate

# Verifikasi
rustc --version
wasm-pack --version

# Membuat project baru
cargo generate --name my-wasm-app https://github.com/rustwasm/wasm-pack-template
cd my-wasm-app

# Struktur project:
# my-wasm-app/
# β”œβ”€β”€ Cargo.toml        ← Rust dependencies
# β”œβ”€β”€ src/
# β”‚   └── lib.rs        ← Rust source code
# └── pkg/              ← Output setelah build (auto-generated)
#     β”œβ”€β”€ my_wasm_app_bg.wasm
#     β”œβ”€β”€ my_wasm_app.js
#     └── package.json
TOML β€” Cargo.toml Configuration
[package]
name = "my-wasm-app"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "rlib"]  # cdylib untuk Wasm output

[dependencies]
wasm-bindgen = "0.2"             # JS ↔ Rust interop
web-sys = { version = "0.3", features = [
  "Document", "Element", "HtmlElement", "Window",
  "console", "Performance",
]}                                # Browser API bindings
js-sys = "0.3"                    # JavaScript built-in bindings

[dev-dependencies]
wasm-bindgen-test = "0.3"        # Testing framework

[profile.release]
opt-level = "s"                   # Optimize for size
lto = true                        # Link-time optimization

3. wasm-bindgen

wasm-bindgen adalah library yang memfasilitasi komunikasi antara Rust dan JavaScript. Tanpa wasm-bindgen, Wasm hanya bisa beroperasi pada angka (i32, f64). Dengan wasm-bindgen, Anda bisa mengirim string, struct, array, dan bahkan memanggil fungsi JavaScript dari Rust.

Rust β€” Fungsi Dasar dengan wasm-bindgen
// src/lib.rs
use wasm_bindgen::prelude::*;

// Ekspor fungsi ke JavaScript
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
    format!("Hello, {}! Dari Rust via WebAssembly πŸ¦€", name)
}

// Fungsi komputasi berat
#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u64 {
    if n <= 1 { return n as u64; }
    let mut a: u64 = 0;
    let mut b: u64 = 1;
    for _ in 2..=n {
        let temp = a + b;
        a = b;
        b = temp;
    }
    b
}

// Fungsi dengan multiple parameters
#[wasm_bindgen]
pub fn process_array(data: &[f64]) -> f64 {
    data.iter().sum::<f64>() / data.len() as f64
}

// Struct yang bisa diakses dari JS
#[wasm_bindgen]
pub struct Calculator {
    value: f64,
}

#[wasm_bindgen]
impl Calculator {
    #[wasm_bindgen(constructor)]
    pub fn new(initial: f64) -> Calculator {
        Calculator { value: initial }
    }

    pub fn add(&mut self, x: f64) {
        self.value += x;
    }

    pub fn multiply(&mut self, x: f64) {
        self.value *= x;
    }

    pub fn get_value(&self) -> f64 {
        self.value
    }
}

// Menggunakan console.log dari Rust
#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace = console)]
    fn log(s: &str);
}

// Macro untuk println!-like logging
macro_rules! console_log {
    ($($t:tt)*) => (log(&format_args!($($t)*)).to_string())
}

#[wasm_bindgen(start)]
pub fn main() {
    console_log!("Rust WASM module initialized! πŸš€");
}
JavaScript β€” Menggunakan Exported Functions
// Import dari package yang di-generate wasm-pack
import init, {
  greet,
  fibonacci,
  Calculator,
  process_array,
} from './pkg/my_wasm_app.js';

async function main() {
  // Inisialisasi Wasm module
  await init();

  // Menggunakan fungsi yang diekspor
  console.log(greet("Budi"));
  // Output: "Hello, Budi! Dari Rust via WebAssembly πŸ¦€"

  // Benchmark: Fibonacci
  const start = performance.now();
  const result = fibonacci(50);
  const elapsed = performance.now() - start;
  console.log(`Fibonacci(50) = ${result}, took ${elapsed.toFixed(2)}ms`);

  // Menggunakan struct
  const calc = new Calculator(100);
  calc.add(50);
  calc.multiply(2);
  console.log(calc.get_value()); // 300

  // Jangan lupa free memory untuk struct yang tidak dipakai
  calc.free();

  // Menggunakan array
  const data = new Float64Array([1.5, 2.3, 3.7, 4.1, 5.9]);
  const avg = process_array(data);
  console.log(`Average: ${avg.toFixed(2)}`); // 3.50
}

main();

4. wasm-pack

wasm-pack adalah tool all-in-one yang mengkompilasi Rust ke Wasm, meng-generate JS glue code, dan menyiapkan package yang siap dipublish ke npm.

Shell β€” Build Commands
# Build untuk web target
wasm-pack build --target web
# Output di folder: pkg/
# - my_wasm_app_bg.wasm     (binary)
# - my_wasm_app.js           (JS glue)
# - my_wasm_app.d.ts         (TypeScript types)
# - package.json

# Build untuk bundler (webpack/vite)
wasm-pack build --target bundler

# Build untuk Node.js
wasm-pack build --target nodejs

# Build untuk no-modules (tanpa bundler)
wasm-pack build --target no-modules

# Build release (optimized)
wasm-pack build --release --target web

# Publish ke npm
wasm-pack login
wasm-pack publish

# Ukuran output:
# debug:   ~500KB - 2MB
# release: ~50KB - 200KB (setelah optimasi)
HTML β€” Menggunakan Wasm di Web
<!DOCTYPE html>
<html>
<body>
  <h1>WebAssembly Demo</h1>
  <div id="output"></div>

  <script type="module">
    import init, { greet, fibonacci } from './pkg/my_wasm_app.js';

    async function run() {
      await init();

      document.getElementById('output').innerHTML = `
        <p>${greet('World')}</p>
        <p>Fibonacci(40) = ${fibonacci(40)}</p>
      `;
    }

    run();
  </script>
</body>
</html>

5. Memory Model

WebAssembly memiliki linear memory β€” blok memori yang bisa diakses dari kedua sisi (Rust dan JS). Memahami memory model sangat penting untuk menghindari bugs dan memory leaks.

Diagram: Wasm Memory Model
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚          WEBASSEMBLY LINEAR MEMORY             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚           Linear Memory Buffer           β”‚  β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”  β”‚  β”‚
β”‚  β”‚  β”‚ Stackβ”‚ Heap β”‚ Data β”‚ Code β”‚ Free  β”‚  β”‚  β”‚
β”‚  β”‚  β”‚      β”‚      β”‚      β”‚      β”‚       β”‚  β”‚  β”‚
β”‚  β”‚  β”‚ ←SP  β”‚      β”‚      β”‚      β”‚       β”‚  β”‚  β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                β”‚
β”‚  JS mengakses melalui: wasm.memory.buffer      β”‚
β”‚  Rust mengakses melalui: pointer/reference     β”‚
β”‚                                                β”‚
β”‚  Transfer data:                                β”‚
β”‚  JS β†’ Rust: copy ke memory, pass pointer       β”‚
β”‚  Rust β†’ JS: baca dari memory, return value     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Rust β€” Shared Memory & Typed Arrays
use wasm_bindgen::prelude::*;

// Mengembalikan Uint8Array ke JS (zero-copy view ke Wasm memory)
#[wasm_bindgen]
pub fn get_image_buffer() -> Vec<u8> {
    // wasm-bindgen otomatis konversi Vec<u8> ke Uint8Array
    let mut buffer = vec![0u8; 1024];
    // Isi buffer dengan data...
    buffer
}

// Menerima Uint8Array dari JS
#[wasm_bindgen]
pub fn process_image(data: &[u8]) -> Vec<u8> {
    // data adalah view ke Wasm memory, bukan copy
    let mut output = Vec::with_capacity(data.len());
    for &byte in data {
        output.push(byte.wrapping_add(1)); // Contoh transformasi
    }
    output
}

// Menerima dan mengembalikan Float64Array
#[wasm_bindgen]
pub fn transform_matrix(input: &[f64], rows: usize, cols: usize) -> Vec<f64> {
    let mut output = vec![0.0f64; rows * cols];
    for i in 0..rows {
        for j in 0..cols {
            // Transpose matrix
            output[j * rows + i] = input[i * cols + j];
        }
    }
    output
}

// Warning: jangan simpan pointer ke Wasm memory dari Rust
// karena memory bisa di-reallocate!

6. JavaScript Interop

Rust β€” Memanggil JS dari Rust
use wasm_bindgen::prelude::*;

// Deklarasi fungsi JS yang bisa dipanggil dari Rust
#[wasm_bindgen]
extern "C" {
    // Memanggil fungsi JS global
    fn alert(s: &str);

    // Mengakses browser APIs
    type HTMLDocument;
    static document: HTMLDocument;

    #[wasm_bindgen(method)]
    fn getElementById(this: &HTMLDocument, id: &str) -> web_sys::Element;

    // Mengakses namespace JS
    #[wasm_bindgen(js_namespace = console)]
    fn log(s: &str);

    #[wasm_bindgen(js_namespace = console, js_name = log)]
    fn log_u32(a: u32);

    // Mengakses property
    #[wasm_bindgen(method, getter)]
    fn innerText(this: &web_sys::Element) -> String;

    // Callback dari Rust ke JS
    type Function;
    #[wasm_bindgen(constructor)]
    fn new(args: &str, body: &str) -> Function;
    #[wasm_bindgen(method)]
    fn call(this: &Function, this_arg: &JsValue, args: &JsValue) -> JsValue;
}

// Menggunakan closures (JS callbacks)
#[wasm_bindgen]
pub fn set_timeout(callback: &js_sys::Function, ms: i32) {
    web_sys::window()
        .unwrap()
        .set_timeout_with_callback_and_timeout_and_arguments_0(
            callback,
            ms,
        )
        .unwrap();
}

// Promise dari Rust
#[wasm_bindgen]
pub async fn fetch_data(url: &str) -> Result<JsValue, JsValue> {
    let window = web_sys::window().unwrap();
    let resp_value = wasm_bindgen_futures::JsFuture::from(
        window.fetch_with_str(url)
    ).await?;

    let resp: web_sys::Response = resp_value.dyn_into()?;
    let json = wasm_bindgen_futures::JsFuture::from(resp.json()?).await?;

    Ok(json)
}

7. Performance & Benchmark

TugasJavaScriptRust/WasmSpeedup
Fibonacci(45)~850ms~12ms~70x
Image blur (1080p)~450ms~15ms~30x
JSON parse (10MB)~200ms~50ms~4x
Matrix multiply (512x)~2000ms~30ms~65x
String search (1M lines)~150ms~8ms~18x
Sorting (1M integers)~300ms~45ms~6x
⚠️ Catatan

Wasm TIDAK selalu lebih cepat dari JavaScript. Untuk tugas yang banyak berinteraksi dengan DOM, JavaScript tetap lebih baik karena tidak ada overhead FFI (Foreign Function Interface). Wasm ideal untuk komputasi murni β€” image processing, encoding, cryptography, game physics.

8. Real-World Use Cases

ProjectTeknologiKegunaan
FigmaRust + WasmDesign editor berperforma tinggi
1PasswordRust + WasmCryptography di browser
Google EarthC++ β†’ Wasm3D globe rendering
FFmpeg.wasmC β†’ WasmVideo encoding di browser
SquooshRust/WasmImage compression
AutoCAD WebC++ β†’ WasmCAD drawing di browser
DenoRustJavaScript runtime (server)

9. Best Practices

βœ… Rust + Wasm Best Practices
  • Optimize for size β€” gunakan opt-level = "s" dan lto = true
  • wasm-opt β€” jalankan wasm-opt -Oz untuk optimasi post-build
  • Minimize FFI calls β€” batch operasi, jangan bolak-balik JS↔Wasm
  • Offload heavy compute β€” gunakan Web Workers untuk Wasm berat
  • Streaming instantiation β€” gunakan WebAssembly.instantiateStreaming()
  • Lazy loading β€” load .wasm hanya saat dibutuhkan
  • Error handling β€” gunakan Result<T, JsValue> untuk error ke JS
  • Testing β€” gunakan wasm-bindgen-test untuk unit test
  • Profile dulu β€” jangan gunakan Wasm untuk semua, hanya yang benar-benar butuh performa
  • web-sys features β€” enable hanya fitur browser API yang dibutuhkan

10. Quiz: Uji Pemahamanmu!

Setelah membaca tutorial WebAssembly dengan Rust, jawablah 5 pertanyaan berikut:

Pertanyaan 1: Apa fungsi utama wasm-bindgen?

a) Mengkompilasi Rust ke binary
b) Mengelola dependencies Rust
c) Menghubungkan Rust/Wasm dengan JavaScript
d) Mengoptimasi ukuran .wasm

Pertanyaan 2: Target apa yang ditambahkan untuk WebAssembly di Rust?

a) wasm64-unknown-unknown
b) wasm32-unknown-unknown
c) wasm32-browser
d) rust-wasm32

Pertanyaan 3: Kapan Wasm LEBIH CEPAT dari JavaScript?

a) Untuk manipulasi DOM
b) Untuk event handling
c) Untuk komputasi murni (image processing, crypto)
d) Untuk semua tugas

Pertanyaan 4: Perintah apa yang mem-build Rust ke Wasm untuk web?

a) cargo build --target wasm
b) rustc --target wasm
c) wasm-pack build --target web
d) wasm build --release

Pertanyaan 5: Apa itu linear memory dalam WebAssembly?

a) Stack memory untuk fungsi
b) Blok memori yang bisa diakses dari Rust dan JS
c) Cache memory browser
d) GPU memory
πŸ” Zoom
100%
🎨 Tema