当对象添加到向量时,Rust wasm_bindgen 对象在 JS 端变为空

标签 rust webassembly wasm-bindgen

我有两个导出到 Javascript 的结构。我可以创建实例并在 JavaScript 中使用它们而不会出现任何错误,但是当我将实例插入 Rust 端的向量时,出现错误“未捕获错误:空指针传递给 Rust”

由于所有权已更改,JS 对象变为 null 是完全正常的,但我还需要保留我的 JavaScript 对象以便在 JavaScript 端进行更改。

是否有任何正确的方法来保持“vect”对象不为空并允许更改?

我添加了一个有效的 example .您可以在浏览器的控制台中看到错误。

使用rust 代码

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
struct Vector3 {
    x: f32,
    y: f32,
    z: f32,
}

#[wasm_bindgen]
impl Vector3 {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Vector3 {
        return Vector3 {
            x: 0.0,
            y: 0.0,
            z: 0.0,
        };
    }
    pub fn get_x(&self) -> f32 {
        self.x
    }
}

#[wasm_bindgen(extends = Object)]
struct Group {
    list: Vec<Vector3>,
}

#[wasm_bindgen]
impl Group {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Group {
        return Group { list: vec![] };
    }

    pub fn add(&mut self, vec: Vector3) {
        self.list.push(vec);
    }
}

JavaScript 代码

let group = new Group();

let list = [];
for (let i = 0; i < 10; i++) {
  let vect = new Vector3();
  list.push(vect);
  group.add(vect);
}

setInterval(() => {
  for (let i = 0; i < list.length; i++) {
    const vect = list[i];
    console.log(vect.get_x());
  }
}, 1000);

最佳答案

你应该非常小心你的数据被复制 - 一个副本在 JS 端,然后在 Rust 的 WASM 端。这里的问题是 Group.add 将值移出,因此一旦您调用 group.add,向量内部“堆指针”(由 wasm-bindgen 生成的互操作代码维护)就会改变并且之前的副本变得无效,因此插入到 list 中的值几乎没有用。

处理这种情况的 Rust-y 方法是保留一个借用值的列表,并显式管理生命周期,以便该列表不会超过其元素。不幸的是,wasm-bindgen 不允许对导出的结构进行显式生命周期声明,因此该选项不可用。

理想情况下,所有与处理向量相关的逻辑都应该只存在于 Rust 中并且对 JavaScript 隐藏。如果您真的需要访问两个位置的矢量,最简单的暴力解决方案是向组添加一个 getter 并将其用作“主副本”。代码如下所示:

#[wasm_bindgen]
#[derive(Copy, Clone)]
pub struct Vector3 {
    x: f32,
    y: f32,
    z: f32,
}

#[wasm_bindgen]
impl Vector3 {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Vector3 {
        Vector3 { x:0.0, y:0.0, z:0.0 }
    }

    pub fn get_x(&self) -> f32 {
        self.x
    }

    pub fn get_y(&self) -> f32 {
        self.y
    }

    pub fn get_z(&self) -> f32 {
        self.z
    }
}

#[wasm_bindgen]
pub struct Group{
    list: Vec<Vector3>,
}

#[wasm_bindgen]
impl Group {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Group {
        Group { list: vec![] }
    }

    pub fn add(&mut self, vec: Vector3) {
        self.list.push(vec);
    }

    pub fn get_at(&self, idx: usize) -> Vector3 {
        self.list[idx]
    }
}

然后,JavaScript 端看起来像:

...

setTimeout(() => {
  for (let i = 0; i < list.length; i++) {
    const vect = group.get_at(i);
    console.log(vect.get_x());
  }
}, 1000);

...

我们完全摆脱了 list

注意:这是一种非常糟糕的列表管理方式,因为每次调用 get_at 都会创建向量的另一个副本,因此如果您的代码需要大量计算那么内存泄漏可能是一个问题。不幸的是,wasm-bindgen 不允许借用返回值,因此如果您需要在一次调用中拥有整个元组,克隆几乎是唯一的选择。

如果你不介意在 JS 端处理许多小调用,那么一个更明显的优化是拆分 get_at 并将其转换为 get_x_at , get_y_at, get_z_at 从而避免了携带 Vector 实例跨越 wasm 边界的需要。

更好的是,也许您可​​以想出一种不同的方法来拆分关注的区域,这样向量就根本不必跨越装配边界。

希望对您有所帮助!

关于当对象添加到向量时,Rust wasm_bindgen 对象在 JS 端变为空,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58785205/

相关文章:

node.js - Rust&Wasm初始设置

rust - 如何在不编辑函数签名的情况下更正 "cannot infer an appropriate lifetime"?

c++ - 我可以使用 Webassembly 从磁盘读取文件吗?

javascript - 在 WebAssembly 中写入文件

rust - 如何将闭包转换为 js_sys::Function?

arrays - 无法将迭代器转换为 js_sys::Array

rust - 为什么 Cow 的定义在 Owned(<B as ToOwned>::Owned) 中使用 "as"?

rust - 仅当满足类型约束条件时才有条件地实现 Rust 特性

javascript - 在 npm 上发布 wasm 库的首选方式是什么?

rust - 使用 wasm-bindgen 对大型 rust 对象进行 Js 绑定(bind)