wasm-bindgen & web-sys — WebAssembly

wasm-bindgen & web-sys Wasm secara default hanya bisa komunikasi dengan angka. wasm-bindgen otomatis generate glue code agar Rust bisa berinteraksi dengan…

wasm-bindgen & web-sys

Wasm secara default hanya bisa komunikasi dengan angka. wasm-bindgen otomatis generate glue code agar Rust bisa berinteraksi dengan JavaScript objects, DOM, dan Web APIs secara ergonomis.

wasm-bindgen: JS ↔ Rust Bridge

use wasm_bindgen::prelude::*;

// Export ke JavaScript
#[wasm_bindgen]
pub fn process_data(input: &str) -> String {
    format!("Processed: {}", input.to_uppercase())
}

// Import dari JavaScript
#[wasm_bindgen]
extern "C" {
    // Panggil console.log
    #[wasm_bindgen(js_namespace = console)]
    fn log(s: &str);

    // Panggil alert
    fn alert(s: &str);

    // Akses window.location.href
    #[wasm_bindgen(js_namespace = window, js_name = location)]
    static LOCATION: JsValue;
}

#[wasm_bindgen]
pub fn greet(name: &str) {
    log(&format!("Halo dari Rust: {}", name));
}

web-sys: DOM Access dari Rust

// Cargo.toml
[dependencies]
wasm-bindgen = "0.2"
web-sys = { version = "0.3", features = [
    "Document", "Element", "HtmlElement",
    "Window", "Node", "console",
    "HtmlCanvasElement", "CanvasRenderingContext2d",
] }
use wasm_bindgen::prelude::*;
use web_sys::{Document, Element, Window};

#[wasm_bindgen]
pub fn create_element(tag: &str, text: &str) -> Result<(), JsValue> {
    // Dapatkan window dan document
    let window: Window = web_sys::window()
        .ok_or("no window")?;
    let document: Document = window.document()
        .ok_or("no document")?;

    // Buat elemen baru
    let element: Element = document.create_element(tag)?;
    element.set_text_content(Some(text));
    element.set_attribute("class", "from-rust")?;

    // Tambahkan ke body
    document.body()
        .ok_or("no body")?
        .append_child(&element)?;

    Ok(())
}

js-sys: JavaScript Built-in Objects

use js_sys::{Array, Date, Math, JSON, Object};

#[wasm_bindgen]
pub fn demo_js_sys() {
    // Math
    let random = Math::random();
    let sqrt = Math::sqrt(144.0); // 12.0

    // Date
    let now = Date::now(); // timestamp ms

    // Array
    let arr = Array::new();
    arr.push(&JsValue::from(1));
    arr.push(&JsValue::from(2));

    // JSON
    let obj = Object::new();
    let json_str = JSON::stringify(&obj).unwrap();
}

Closure / Callback Pattern

use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;

#[wasm_bindgen(start)]
pub fn main() -> Result<(), JsValue> {
    let window = web_sys::window().unwrap();
    let document = window.document().unwrap();
    let button = document.get_element_by_id("my-btn").unwrap();

    // Rust closure sebagai JS event listener
    let closure = Closure::wrap(Box::new(move || {
        web_sys::console::log_1(&"Button clicked! (from Rust)".into());
    }) as Box<dyn FnMut()>);

    button.add_event_listener_with_callback(
        "click",
        closure.as_ref().unchecked_ref(),
    )?;

    closure.forget(); // ⚠️ Jangan drop closure
    Ok(())
}

Tips

Yang akan kamu pelajari