rust/wasm from js

Some learnings related to using rust/wasm from js...


Call rust/wasm from js...

Some learnings related to using rust/wasm from js...

  1. When you create Vec3 object, you can manipulate mostly like you expect

    const vec3 = new Vec3(0, 1, 2);
    vec3.x = 42; // This works because we have a rust object
  2. wasm_bindgen can not return references due to rust's borrowing... So setting properties on sub objects fail because they are copies instead of a reference.

    const camera = new Camera(45, 1.7, 1, 1000);
    camera.position = new Vec3(3, 42, 3); // This works because we are replacing the whole object
     
    // Fails quietly!!!!
    camera.position.x = 42; // position is a copy, not a reference...

    Can't fix it either, because we can't return a reference due to the borrow checking

    #[cfg_attr(target_arch = "wasm32", wasm_bindgen(getter))]
    pub fn position(&self) -> &Vec3 { // error: cannot return a borrowed ref with #[wasm_bindgen]
      self.position
    }

vec3.rs

use serde::{Deserialize, Serialize};
use serde_wasm_bindgen::from_value;
 
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*; // Use serde_json for JSON serialization
 
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
#[derive(Serialize, Deserialize, Clone, Copy)]
pub struct Vec3 {
    #[serde(default)]
    pub x: f32,
    #[serde(default)]
    pub y: f32,
    #[serde(default)]
    pub z: f32,
}
 
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
impl Vec3 {
    #[cfg_attr(target_arch = "wasm32", wasm_bindgen(constructor))]
    pub fn new(x: f32, y: f32, z: f32) -> Vec3 {
        Self { x, y, z }
    }
 
    #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = toString))]
    pub fn to_string(&self) -> String {
        format!("Vec3(x: {}, y: {}, z: {})", self.x, self.y, self.z)
    }
 
    #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = toJSON))]
    pub fn to_json(&self) -> Result<JsValue, JsValue> {
        Ok(serde_wasm_bindgen::to_value(&self)?)
    }
 
    // Static method to create a Vec3 instance from a JsValue
    #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = fromJSON))]
    pub fn from_json(value: JsValue) -> Result<Vec3, JsValue> {
        from_value(value).map_err(|e| JsValue::from_str(&e.to_string()))
    }
}

camera.rs

use crate::vec3::Vec3;
use serde::{Deserialize, Serialize};
 
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*; // Use serde_json for JSON serialization
 
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
#[derive(Serialize, Deserialize)]
pub struct Camera {
    fov: f32,
    aspect: f32,
    near: f32,
    far: f32,
    position: Vec3,
}
 
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
impl Camera {
    #[cfg_attr(target_arch = "wasm32", wasm_bindgen(constructor))]
    pub fn new(fov: f32, aspect: f32, near: f32, far: f32) -> Self {
        Camera {
            fov,
            aspect,
            near,
            far,
            position: Vec3 {
                x: 0.0,
                y: 0.0,
                z: 0.0,
            },
        }
    }
 
    // Setter for y
    #[cfg_attr(target_arch = "wasm32", wasm_bindgen(setter))]
    pub fn set_position(&mut self, value: Vec3) {
        self.position = value;
    }
 
    #[cfg_attr(target_arch = "wasm32", wasm_bindgen(getter))]
    pub fn position(&self) -> Vec3 {
        self.position
    }
 
    #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = toString))]
    pub fn to_string(&self) -> String {
        format!(
            "Camera(fov: {}, aspect: {}, near: {}, far: {})",
            self.fov, self.aspect, self.near, self.far
        )
    }
 
    #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = toJSON))]
    pub fn to_json(&self) -> Result<JsValue, JsValue> {
        Ok(serde_wasm_bindgen::to_value(&self)?)
    }
}