Какой лучший способ борьбы с замыканиями в WebAssembly с помощью Rust вместо использования утечки памяти? - PullRequest
8 голосов
/ 21 января 2020

Когда вы предоставляете обратные вызовы для JavaScript с использованием Закрытия , что является лучшим способом избежать их освобождения? Руководство по wasm-bindgen предлагает использовать .forget, но допускает, что это, по сути, утечка памяти.

Обычно мы сохраняем дескриптор, чтобы позже его уронили в подходящее время но сейчас мы хотим, чтобы это был глобальный обработчик, поэтому мы используем метод forget, чтобы удалить его, не делая недействительным замыкание. Обратите внимание, что это утечка памяти в Rust, поэтому это следует делать разумно!

Это намекает на сохранение закрытия до тех пор, пока его не удастся сбросить. В ответе alexcrichton на на предыдущий вопрос он упоминает ...

[...], если он [...] вызывается только один раз, затем вы можете использовать Rc / RefCell, чтобы опустить Closure внутри самого затвора (используя некоторые внутренние изменчивые махинации)

Но он не приводит пример этого метода.

В документации Закрытия также приведен пример возврата ссылки на замыкание в контекст JavaScript, чтобы он мог обрабатывать, когда освобождать ссылку.

Если бы мы здесь отбросили cb, это вызвало бы исключение, возникающее при истечении интервала. Вместо этого мы возвращаем наш дескриптор обратно в JS, поэтому JS может решить, когда отменить интервал и освободить закрытие.

Я бы также предположил, что есть способы чтобы избежать этой проблемы, используйте такие функции, как время жизни или макрос #[wasm_bindgen] в функции publi c, но мне сложно понять, как это сделать.

Вопрос в том, что Есть ли альтернативы использованию .forget с замыканиями, которые передаются обратно в JavaScript из Rust, и могу ли я увидеть несколько простых примеров каждого используемого параметра?

1 Ответ

1 голос
/ 21 января 2020

Я недавно создал небольшое коммерческое приложение и несколько недель застревал в нем, и был очень взволнован, когда заработал. В итоге я использовал Closure.once_into_ js. Однако здесь также есть оговорка: «Единственный способ, которым FnOnce освобождается, - это вызов функции JavaScript. Если функция JavaScript никогда не вызывается, тогда FnOnce и все, с чем она закрывается, будут просачиваться». Так что, если обратный вызов вызван, все должно быть в порядке, но если нет, все равно есть утечка памяти. Я нашел стиль программирования довольно приятным. Я сопоставил функции JavaScript с Rust следующим образом:

#[wasm_bindgen]
fn getSomething(details: &JsValue, callback: JsValue);

pub fn get_something(details: &Details, callback: impl Fn(Option<String>) + 'static){
    getSomething(&serde_wasm_bindgen::to_value(details).unwrap(), Closure::once_into_js(move |v: JsValue| 
        callback(serde_wasm_bindgen::from_value(v).unwrap())   
    ));
}

И затем я могу использовать его из Rust в своем приложении следующим образом:

let callback = move |id| {
};
get_something(&details, callback);

Я определил обратные вызовы как stati c подразумевают функции и затем перемещают значения в.

...