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?
| Keunggulan | Penjelasan | Dampak |
|---|---|---|
| Performa | Wasm 10-100x lebih cepat dari JS untuk komputasi | Real-time processing |
| Memory Safety | Rust tanpa garbage collector = predictable performance | Tidak ada GC pauses |
| Bundle Size | Rust menghasilkan .wasm yang kecil | Fast download |
| Ecosystem | wasm-pack, wasm-bindgen, web-sys | DX yang baik |
| Reusability | Kode Rust yang sama bisa jalan di server dan browser | Code sharing |
Arsitektur WebAssembly
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β 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
# 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
[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.
// 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! π");
}
// 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.
# 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)
<!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.
ββββββββββββββββββββββββββββββββββββββββββββββββββ β 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 β ββββββββββββββββββββββββββββββββββββββββββββββββββ
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
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
| Tugas | JavaScript | Rust/Wasm | Speedup |
|---|---|---|---|
| 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 |
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
| Project | Teknologi | Kegunaan |
|---|---|---|
| Figma | Rust + Wasm | Design editor berperforma tinggi |
| 1Password | Rust + Wasm | Cryptography di browser |
| Google Earth | C++ β Wasm | 3D globe rendering |
| FFmpeg.wasm | C β Wasm | Video encoding di browser |
| Squoosh | Rust/Wasm | Image compression |
| AutoCAD Web | C++ β Wasm | CAD drawing di browser |
| Deno | Rust | JavaScript runtime (server) |
9. Best Practices
- Optimize for size β gunakan
opt-level = "s"danlto = true - wasm-opt β jalankan
wasm-opt -Ozuntuk 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-testuntuk 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: