Объект Rust wasm_bindgen становится нулевым в сторону JS, когда объект добавляется в вектор - PullRequest
2 голосов
/ 10 ноября 2019

У меня есть две структуры, экспортированные в Javascript. Я могу создавать экземпляры и использовать их в JavaScript без каких-либо ошибок, но когда я помещаю экземпляры в вектор на стороне Rust, я получаю сообщение об ошибке «Uncaught Error: нулевой указатель передан в rust»

Поскольку владение изменено,Совершенно нормально, что объекты JS обнуляются, но мне также нужно сохранить свои объекты JavaScript, чтобы изменить что-то на стороне JavaScript.

Есть ли какой-нибудь правильный способ сохранить объект "vect" не равным NULL и открытым для изменений?

Я добавил рабочий пример . Вы можете увидеть ошибку в консоли вашего браузера.

Код ржавчины

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);

1 Ответ

1 голос
/ 13 ноября 2019

Вы должны быть крайне осторожны с дублированием ваших данных - одна копия находится на стороне JS, а затем на стороне WASM в Rust. Проблема здесь в том, что Group.add перемещает значение так, что после того, как вы вызвали group.add, внутренний внутренний указатель кучи (поддерживаемый кодом взаимодействия, сгенерированным wasm-bindgen) изменится, и предыдущая копия станет недействительной, поэтомузначения, вставленные в list, в значительной степени бесполезны.

Rust-y способ справиться с этой ситуацией состоит в том, чтобы вместо этого сохранить список заимствованных значений и явно управлять временем жизни, чтобы этот списокне переживает своих элементов. К сожалению, wasm-bindgen не допускает явных объявлений времени жизни экспортируемых структур, поэтому эта опция отсутствует.

В идеале вся логика, связанная с работой с векторами, должна существовать только в Rust и быть скрытой от JavaScript. Если вам действительно нужно иметь доступ к векторам в обоих местах, самое простое грубое решение - добавить геттер в группу и использовать его в качестве "мастер-копии". Код будет выглядеть так:

#[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.

Еще лучше, возможно, вы могли бы придумать другой способ разбиения проблемных областей, чтобы векторам вообще не приходилось пересекать границу сборки.

Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...