Belajar WebAssembly - Rust + wasm-pack
Episode 7 of 23

Belajar WebAssembly - Rust + wasm-pack

Membangun pipeline WebAssembly dari Rust: cargo new --lib, menambah target wasm32-unknown-unknown, wasm-pack build dengan target web, bundler, dan nodejs, serta memahami struktur pkg/ hasil build

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setelah di episode 6 kita memahami interop JS–Wasm dan peran bindings seperti wasm-bindgen — pada episode ini kita membangun pipeline Rust ke WebAssembly yang sesungguhnya. Ini adalah titik di mana kalian berhenti menulis WAT manual dan mulai menulis kode produksi dalam bahasa yang manusiawi, lalu membiarkan toolchain mengurus bytecode-nya.

Mengapa Rust menjadi pilihan utama ekosistem Wasm? Karena Rust punya zero-cost abstractions — kode yang ditulis abstrak namun dikompilasi secepat kode C — plus keamanan memory tanpa garbage collector, yang sangat cocok dengan model linear memory Wasm. Kombinasi inilah yang membuat wasm-pack menjadi toolchain paling matang untuk pengembangan Wasm frontend.

Menyiapkan Proyek

cargo new --lib

Buat proyek pustaka Rust baru:

Buat proyek Rust baru
cargo new --lib wasm-math
cd wasm-math

--lib membuat src/lib.rs — bukan binary — karena output kita akan menjadi modul Wasm yang dipanggil dari luar, bukan program yang berdiri sendiri.

Tambahkan Target dan Dependensi

Pastikan target wasm32-unknown-unknown terinstall (episode 0), lalu tambahkan wasm-bindgen:

Install target dan dependensi
rustup target add wasm32-unknown-unknown
cargo add wasm-bindgen
cargo add wasm-pack

Ganti isi src/lib.rs:

src/lib.rs
use wasm_bindgen::prelude::*;
 
#[wasm_bindgen]
pub fn tambah(a: i32, b: i32) -> i32 {
    a + b
}
 
#[wasm_bindgen]
pub fn fib(n: u32) -> u32 {
    match n {
        0 | 1 => n,
        _ => fib(n - 1) + fib(n - 2),
    }
}
 
#[wasm_bindgen]
pub fn sapaan(nama: &str) -> String {
    format!("Halo, {nama}! Selamat datang di WebAssembly.")
}

Tiga fungsi ini menunjukkan tiga tingkat marshalling wasm-bindgen:

  • tambah — primitif i32, nol biaya marshalling.
  • fib — rekursif, murni komputasi, contoh workload berat yang "diangkat" dari JS.
  • sapaan&strString: wasm-bindgen mengubahnya menjadi pointer+panjang di memory, dan otomatis menghasilkan wrapper JS yang menerima string biasa.

Build dengan wasm-pack

Target web

Build untuk browser modern (ES modules)
wasm-pack build --target web

Output di folder pkg/:

plaintext
pkg/
├── wasm_math_bg.wasm     # bytecode binary
├── wasm_math_bg.wasm.d.ts
├── wasm_math.js          # JS glue (binding)
├── wasm_math.d.ts        # type definitions
└── package.json          # metadata paket

wasm_math.js adalah glue code yang dihasilkan wasm-bindgen — ia mengurus instansiasi, marshalling, dan memory management. Kalian tidak perlu membaca atau mengubahnya.

Target bundler vs nodejs

--target web menghasilkan module ES tanpa dependensi. Ada dua target lain yang penting:

Build untuk bundler atau Node.js
wasm-pack build --target bundler   # untuk webpack/vite
wasm-pack build --target nodejs    # untuk Node.js/SSR
TargetDipakai untukFitur
webBrowser modern (import langsung)ES module murni
bundlerWebpack/Vite/RollupOptimasi tree-shaking
nodejsNode.js & SSRrequire/ESM dengan interop Node

Menggunakan Hasil Build

Di browser (target web)

HTMLindex.html
<script type="module">
  import init, { tambah, sapaan } from "./pkg/wasm_math.js"
 
  await init()
  console.log(tambah(2, 3))
  console.log(sapaan("Rizky"))
</script>

Perhatikan await init() — glue code harus diinisialisasi dulu (memuat dan menginstansiasi .wasm) sebelum fungsi bisa dipanggil.

Di Node.js (target nodejs)

test-node.mjs
import init, { tambah, fib } from "./pkg/wasm_math.js"
 
await init()
console.log("tambah:", tambah(40, 2))
console.log("fib(20):", fib(20))
Jalankan di Node.js
node test-node.mjs

Output:

text
tambah: 42
fib(20): 6765

Fungsi fib dijalankan sepenuhnya di dalam bytecode Wasm — kecepatan komputasi native di dalam JavaScript sandbox.

Tip

Untuk development cepat, kalian bisa mengekspor fungsi Wasm ke browser tanpa bundler sama sekali memakai target web + import.meta.url di <script type="module">. Bundler baru perlu saat proyek tumbuh besar.

Memahami pkg/ Lebih Dalam

wasm_math.js berisi tiga hal penting:

  1. Instansiasi — memuat .wasm, memanggil WebAssembly.instantiate, dan melempar error yang bisa dibaca.
  2. Marshalling — mengubah &str menjadi pointer+length di memory linear, dan String kembali ke string JS.
  3. Exports — fungsi wrapper untuk setiap #[wasm_bindgen] yang kalian tulis.

Jika kalian ingin melihat WAT di baliknya (ingat episode 4):

Intip WAT hasil compile
wasm-tools print pkg/wasm_math_bg.wasm | head -40

Ini menghubungkan apa yang kalian pelajari di episode 3-5 dengan apa yang compiler Rust hasilkan.

Common Pitfalls

  • Lupa target — jika cargo build menghasilkan file .rlib, bukan .wasm, cek bahwa kalian memakai --target wasm32-unknown-unknown (via wasm-pack).
  • Lupa #[wasm_bindgen] — fungsi tanpa atribut tidak diexport ke JS.
  • Tipe tidak didukung&str/String didukung, tapi &mut [u8] butuh #[wasm_bindgen] pada struct wrapper; pelajari pola boxed slice untuk buffer.
  • Menjalankan paket bundler di Node — hasil --target bundler tidak bisa langsung import di Node; pakai target nodejs.
  • Init async — memanggil fungsi sebelum await init() menghasilkan error; inisialisasi harus selesai dulu.

Warning

Jangan commit folder pkg/ ke repositori untuk paket yang dipublikasikan — jalankan wasm-pack build sebagai bagian dari pipeline build (misal npm run build), atau gunakan wasm-pack publish yang mengurus versi dengan benar.

Penutup

Pada episode 7 ini, kalian telah membangun pipeline Rust → WebAssembly yang lengkap dengan wasm-pack dan memanggilnya dari browser maupun Node.js.

Inti yang harus dibawa pulang:

  • cargo new --lib + dependensi wasm-bindgen adalah titik awal.
  • wasm-pack build --target web|bundler|nodejs menghasilkan .wasm + JS glue di pkg/.
  • Setiap fungsi #[wasm_bindgen] otomatis mendapat wrapper JS dengan marshalling.
  • Panggil await init() dulu sebelum memakai fungsi apa pun.

Di episode 8 selanjutnya kita akan menjelajahi jalur C/C++ dengan Emscripten — instalasi emsdk, compile hello.c menjadi .html/.js/.wasm, penggunaan EMSCRIPTEN_KEEPALIVE, embind untuk class binding, dan filesystem MEMFS. Sampai jumpa di episode 8!