有什么更好的方法来使用 Rust 处理 WebAssembly 中的闭包,而不是使用遗忘和内存泄漏?

What's a better way to deal with closures in WebAssembly with Rust instead of using forget and leaking memory?

提问人: 提问时间:1/21/2020 更新时间:3/23/2023 访问量:1762

问:

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

通常,我们会存储句柄,以便稍后在适当的时间删除,但现在我们希望它是一个全局处理程序,因此我们使用该方法删除它而不会使闭包无效。请注意,这是在 Rust 中泄漏内存,所以这应该明智地完成!forget

它暗示将闭包存储到适合删除的时间。在alexcrichton上一个问题的回答中,他提到......

[...]如果它只调用一次,那么你可以使用 / 来删除闭包本身的内部(使用一些内部可变性恶作剧)RcRefCellClosure

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

Closure 文档还给出了一个示例,该示例将对闭包的引用返回给 JavaScript 上下文,以便它处理何时释放引用。

如果我们要放到这里,则每当间隔过去时,都会导致引发异常。取而代之的是,我们将句柄返回给 JS,以便 JS 可以决定何时取消间隔并解除分配闭包。cb

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

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

Rust 闭包 webassembly wasm-bindgen

评论


答:

0赞 Cameron Taggart 1/21/2020 #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 函数,然后将值移入。

0赞 zxch3n 3/23/2023 #2

当我确定它只会被调用一次时,我会使用它

let promise = Promise::resolve(&JsValue::NULL);
let ob = observer.clone();
type C = Closure<dyn FnMut(JsValue)>;
let drop_handler: Rc<RefCell<Option<C>>> = Rc::new(RefCell::new(None));
let copy = drop_handler.clone();
let closure = Closure::once(move |_: JsValue| {
    ob.call1(
        &Event {
            local: e.local,
            origin: e.origin.clone(),
        }
        .into(),
    );

    drop(copy);
});
let _ = promise.then(&closure);
drop_handler.borrow_mut().replace(closure);