rust - 当对象添加到向量时,Rust wasm_bindgen 对象在 JS 端变为 null
问题描述
我有两个导出到 Javascript 的结构。我可以创建实例并在 JavaScript 中使用它们而不会出现任何错误,但是当我将实例推送到 Rust 端的向量中时,出现错误“未捕获的错误:传递给 rust 的空指针”
由于所有权已更改,JS 对象变为 null 是完全正常的,但我还需要保留我的 JavaScript 对象以更改 JavaScript 方面的内容。
是否有任何正确的方法来保持“vect”对象不为空并对更改开放?
我添加了一个工作示例。您可以在浏览器的控制台中看到错误。
锈代码
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 隐藏。如果您确实需要访问这两个地方的向量,最简单的暴力解决方案是向 Group 添加一个 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 边界的需要。
更好的是,也许您可以想出一种不同的方法来分割关注区域,这样向量就不必跨越装配边界。
希望有帮助!
推荐阅读
- python - 如何在 python 3.x 中覆盖多行
- python - 使用pygame在python中计数循环无法正常工作
- php - 如何在php中显示当前月份和下个月日期?
- merge - 合并数据集时如何防止 SAS 更改值
- php - PHP 检查数组中存在的元素
- python - 为什么要重塑 VGG_UNet 分割模型的最后几层?
- json - 在 asp.net mvc 中使用 ajax 显示复杂的 JSON 数据
- javascript - 如何显示服务器中的表情符号
- java - java中的切换方法
- python - 需要帮助在 python webbrowser.open 中放置一个verable