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

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.
Buat proyek pustaka 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.
Pastikan target wasm32-unknown-unknown terinstall (episode 0), lalu tambahkan wasm-bindgen:
rustup target add wasm32-unknown-unknown
cargo add wasm-bindgen
cargo add wasm-packGanti isi 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 — &str → String: wasm-bindgen mengubahnya menjadi pointer+panjang di memory, dan otomatis menghasilkan wrapper JS yang menerima string biasa.wasm-pack build --target webOutput di folder pkg/:
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 paketwasm_math.js adalah glue code yang dihasilkan wasm-bindgen — ia mengurus instansiasi, marshalling, dan memory management. Kalian tidak perlu membaca atau mengubahnya.
--target web menghasilkan module ES tanpa dependensi. Ada dua target lain yang penting:
wasm-pack build --target bundler # untuk webpack/vite
wasm-pack build --target nodejs # untuk Node.js/SSR| Target | Dipakai untuk | Fitur |
|---|---|---|
web | Browser modern (import langsung) | ES module murni |
bundler | Webpack/Vite/Rollup | Optimasi tree-shaking |
nodejs | Node.js & SSR | require/ESM dengan interop Node |
<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.
import init, { tambah, fib } from "./pkg/wasm_math.js"
await init()
console.log("tambah:", tambah(40, 2))
console.log("fib(20):", fib(20))node test-node.mjsOutput:
tambah: 42
fib(20): 6765Fungsi 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.
wasm_math.js berisi tiga hal penting:
.wasm, memanggil WebAssembly.instantiate, dan melempar error yang bisa dibaca.&str menjadi pointer+length di memory linear, dan String kembali ke string JS.#[wasm_bindgen] yang kalian tulis.Jika kalian ingin melihat WAT di baliknya (ingat episode 4):
wasm-tools print pkg/wasm_math_bg.wasm | head -40Ini menghubungkan apa yang kalian pelajari di episode 3-5 dengan apa yang compiler Rust hasilkan.
cargo build menghasilkan file .rlib, bukan .wasm, cek bahwa kalian memakai --target wasm32-unknown-unknown (via wasm-pack).#[wasm_bindgen] — fungsi tanpa atribut tidak diexport ke JS.&str/String didukung, tapi &mut [u8] butuh #[wasm_bindgen] pada struct wrapper; pelajari pola boxed slice untuk buffer.--target bundler tidak bisa langsung import di Node; pakai target nodejs.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.
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/.#[wasm_bindgen] otomatis mendapat wrapper JS dengan marshalling.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!