用 Rust 处理 WebAssembly 中的闭包而不是使用忘记和泄漏内存有啥更好的方法?

Posted

技术标签:

【中文标题】用 Rust 处理 WebAssembly 中的闭包而不是使用忘记和泄漏内存有啥更好的方法?【英文标题】:What's a better way to deal with closures in WebAssembly with Rust instead of using forget and leaking memory?用 Rust 处理 WebAssembly 中的闭包而不是使用忘记和泄漏内存有什么更好的方法? 【发布时间】:2020-05-07 04:53:32 【问题描述】:

当使用Closures 向 javascript 提供回调时,有什么更好的方法来避免释放它们? wasm-bindgen guide 建议使用.forget,但承认这本质上是内存泄漏。

通常我们会存储句柄,以便以后在适当的时候删除,但现在我们希望它是一个全局处理程序,所以我们使用forget 方法来删​​除它,而不会使闭包失效。请注意,这会在 Rust 中泄漏内存,因此应谨慎行事!

它暗示将闭包存储到适合删除的时候。在alexcrichton's answer 到a previous question 中,他提到...

[...] 如果它 [...] 只被调用一次,那么您可以使用 Rc/RefCellClosure 放入闭包本身(使用一些内部可变性恶作剧)

但他没有提供这种方法的例子。

Closure documentation 还提供了一个示例,将闭包的引用返回给 JavaScript 上下文,让它处理何时释放引用。

如果我们在此处删除cb,它将导致每当间隔过去时引发异常。相反,我们我们的句柄返回给 JS,这样 JS 就可以决定何时取消间隔并解除分配闭包。

我还想象有一些方法可以在公共函数上使用生命周期或 #[wasm_bindgen] 宏等功能来避免这个问题,但我很难弄清楚如何做到这一点。

我的问题是,除了使用 .forget 和从 Rust 传回 JavaScript 的闭包之外,还有哪些替代方法,我可以看看每个使用中的选项的一些简单示例吗?

【问题讨论】:

【参考方案1】:

我最近构建了一个小型商业应用程序,并在这个应用程序上卡了几个星期,当我开始工作时真的很兴奋。我最终使用了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);

我将回调定义为静态 impl 函数,然后将值移入。

【讨论】:

以上是关于用 Rust 处理 WebAssembly 中的闭包而不是使用忘记和泄漏内存有啥更好的方法?的主要内容,如果未能解决你的问题,请参考以下文章

Rust开发WebAssembly在Html和Vue中的应用后篇

Rust & WebAssembly 翻译系列 为什么选择Rust和WebAssembly?

入门 Rust 开发 WebAssembly

抢先一步,Rust构建版支持直接编译WebAssembly

如何在编译为 WebAssembly 的 Rust 库中使用 C 库?

在 Rust 程序和嵌入式 WebAssembly 运行时之间进行通信的最佳实践是啥?