While JavaScript has a garbage-collected heap, WebAssembly has a linear memory space. Nevertheless using a JavaScript ArrayBuffer, we can read and write to WebAssembly’s memory space.
lib.rs:
#[macro_use] extern crate cfg_if; extern crate wasm_bindgen; use wasm_bindgen::prelude::*; cfg_if! { // When the `console_error_panic_hook` feature is enabled, we can call the // `set_panic_hook` function to get better error messages if we ever panic. if #[cfg(feature = "console_error_panic_hook")] { extern crate console_error_panic_hook; use console_error_panic_hook::set_once as set_panic_hook; } } cfg_if! { // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global // allocator. if #[cfg(feature = "wee_alloc")] { extern crate wee_alloc; #[global_allocator] static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; } } // Definitions of the functionality available in JS, which wasm-bindgen will // generate shims for today (and eventually these should be near-0 cost!) // // These definitions need to be hand-written today but the current vision is // that we'll use WebIDL to generate this `extern` block into a crate which you // can link and import. There's a tracking issue for this at // https://github.com/rustwasm/wasm-bindgen/issues/42 // // In the meantime these are written out by hand and correspond to the names and // signatures documented on MDN, for example #[wasm_bindgen] extern "C" { type HTMLDocument; static document: HTMLDocument; #[wasm_bindgen(method)] fn createElement(this: &HTMLDocument, tagName: &str) -> Element; #[wasm_bindgen(method, getter)] fn body(this: &HTMLDocument) -> Element; type Element; #[wasm_bindgen(method, setter = innerHTML)] fn set_inner_html(this: &Element, html: &str); #[wasm_bindgen(method, js_name = appendChild)] fn append_child(this: &Element, other: Element); #[wasm_bindgen(js_namespace = console)] fn log(msg: &str); } macro_rules! log { ($($t:tt)*) => (log(&format!($($t)*))) }
#[wasm_bindgen] pub struct Color { red: u8, green: u8, blue: u8, } #[wasm_bindgen] pub struct Image { pixels: Vec<Color>, } #[wasm_bindgen] impl Image { pub fn new() -> Image { let color1 = Color { red: 255, green: 0, blue: 0, }; let color2 = Color { red: 60, green: 70, blue: 90, }; let pixels = vec![color1, color2]; Image { pixels } } pub fn pixels_ptr(&self) -> *const Color { self.pixels.as_ptr() } }
We create a Image object, which has two functions, new() and pixels_ptr() which can be called by javascript. new() is a constructor function, pixels_ptr() is a instant method.
app.js:
import { memory } from "../crate/pkg/rust_webpack_bg"; import { Image } from "../crate/pkg/rust_webpack"; const image = Image.new(); const pixelsPointer = image.pixels_ptr(); const pixels = new Uint8Array(memory.buffer, pixelsPointer, 6); console.log(pixels); function numToHex(value) { const hex = value.toString(16); return hex.length === 1 ? `0${hex}` : hex; } function drawPixel(x, y, color) { const ctx = canvas.getContext("2d"); ctx.fillStyle = `#${numToHex(color[0])}${numToHex(color[1])}${numToHex( color[2] )}`; ctx.fillRect(x, y, 100, 100); } const canvas = document.createElement("canvas"); document.body.appendChild(canvas); drawPixel(0, 0, pixels.slice(0, 3)); drawPixel(100, 0, pixels.slice(3, 6));