¿Cuál es una mejor manera de lidiar con los cierres en WebAssembly con Rust en lugar de usar olvidar y perder memoria?

8

Al proporcionar devoluciones de llamada a JavaScript usando Closures , ¿cuál es una mejor manera de lidiar con evitar liberarlos? La guía wasm-bindgen sugiere usar .forget, pero admite que eso es esencialmente una pérdida de memoria.

Normalmente, almacenaríamos el identificador para luego descartarlo en el momento adecuado, pero por ahora queremos que sea un controlador global, por lo que utilizamos el forgetmétodo para descartarlo sin invalidar el cierre. Tenga en cuenta que esto está perdiendo memoria en Rust, ¡así que esto debe hacerse con prudencia!

Sugiere almacenar el cierre hasta el momento en que sea apropiado dejarlo caer. En la respuesta de alexcrichton a una pregunta anterior , menciona ...

[...] si se invoca [...] solo una vez, entonces puede usar Rc/ RefCellpara soltar el Closureinterior del cierre (usando algunas travesuras de mutabilidad interior)

Pero él no proporciona un ejemplo de este método.

La documentación de cierre también da un ejemplo de devolver la referencia al cierre al contexto de JavaScript para permitirle manejar cuándo liberar la referencia.

Si tuviéramos que dejarlo cbaquí, provocaría una excepción cada vez que transcurriera el intervalo. En cambio, devolvemos nuestro identificador a JS para que JS pueda decidir cuándo cancelar el intervalo y desasignar el cierre.

También me imagino que hay formas de usar características como vidas o la #[wasm_bindgen]macro en una función pública para evitar este problema también, pero tengo problemas para descubrir cómo hacerlo de esa manera.

Mi pregunta es, ¿cuáles son las alternativas al uso .forgetcon cierres que se están transfiriendo a JavaScript desde Rust, y puedo ver algunos ejemplos simples de cada opción en uso?

dinamita
fuente

Respuestas:

1

Recientemente construí una pequeña aplicación comercial y me quedé atrapado en esto durante semanas y me emocioné mucho cuando empecé a funcionar. Terminé usando Closure.once_into_js . Sin embargo, eso también tiene la advertencia de que "la única forma en que FnOnce se desasigna es llamando a la función JavaScript. Si nunca se llama a la función JavaScript, entonces FnOnce y todo lo que cierra se filtrará". Entonces, si se llama a la devolución de llamada, todo debería estar bien, pero si no, todavía hay una pérdida de memoria. El estilo de programación me pareció bastante agradable. Mapeé las funciones de JavaScript a Rust de esta manera:

#[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())   
    ));
}

Y luego puedo usarlo desde Rust en mi aplicación así:

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

Definí las devoluciones de llamada como funciones implícitas estáticas y luego moví los valores.

Cameron Taggart
fuente