I'm still working through this issue with memory leaking (OOM) issues and during my refactoring came across this situation:
One of my long-lived closures contains another long-lived closure.
The issue for me is, right now I can create a closure within a method, push it to a vec
and I can drop it in another method (which represents a different view). -- things were looking up for a while.
But what about the closure that exists within a closure? It's a wasm_bindgen
closure
over a box
over a move |progressEvent| {}
- I can't update the closure-array on
Self
directly because I can't moveSelf
into the closure; - Updating a temporary variable will trigger a move and the lack of a
copy
trait. - Same deal if I pass in a variable via the caller / parent method.
- Same deal if the closure-array resides in a different Struct.
- Using
std::mem::take
had no effect that I could see (array not updated). - And so on....
I was hoping to do this without using refcell
but I'm not good at that yet. Is there no other way?
My next idea is to break up the closures entirely (return early from the parent closure with the reader
, and then pass it to a separate method on self where the child closure would no longer be nested.
error[E0382]: use of moved value: `p`
--> src/modules/templates/settings.rs:329:18
|
47 | let mut p = vec![];
| ----- move occurs because `p` has type `Vec<{closure@src/modules/templates/settings.rs:265:17: 265:44}>`, which does not implement the `Copy` trait
...
259 | let data_import_cl = Closure::<dyn FnMut(Event)>::new(move |event: Event| {
| ------------------- value moved into closure here
...
285 | p.push(progress_event.clone());
| - variable moved due to use in closure
...
329 | self.add(p);
| ^ value used here after move
Stripped down method:
pub struct Template {
// ...snip...
pub plisteners: Vec<Closure<dyn FnMut(ProgressEvent)>>,
// ...snip...
}
impl Template {
pub fn add(&mut self, events: Vec<impl FnMut(ProgressEvent) + 'static>) {
// TEST: INDIRECT PUSH TO PLISTENERS
for event in events {
self.plisteners.push(Closure::wrap(Box::new(event) as Box<dyn FnMut(ProgressEvent)>));
}
}
// ...snip...
pub fn view_settings(&mut self) {
// ...snip...
let mut p = vec![];
// TEST: INDIRECT PUSH TO PLISTENERS
let data_import_cl = Closure::<dyn FnMut(Event)>::new(move |event: Event| {
event.prevent_default();
let reader = FileReader::new().expect("Some: `FileReader`");
let progress_event = {
let reader_copy = reader.clone();
move |event: ProgressEvent| {
// ...snip...
Template::data_import_process(&reader_copy); // no `self`
// ...snip...
}
};
let add_event_listeners = || {
let reader_et: EventTarget = reader.clone().into();
let reader_cl = Closure::wrap(Box::new(progress_event.clone()) as Box<dyn FnMut(ProgressEvent)>);
// ^^^^^^^^^^^^^^^
reader_et
.add_event_listener_with_callback("progress", reader_cl.as_ref().unchecked_ref())
.expect("Failed: `unchecked_ref()`");
reader_et
.add_event_listener_with_callback("loadend", reader_cl.as_ref().unchecked_ref())
.expect("Failed: `unchecked_ref()`");
// ^^^^^^^^^^^^^^^
// THE CLOSURES I WANT TO SAVE REFERENCES TO AND DROP
reader_cl.forget();
};
p.push(progress_event.clone());
// p.push((reader.clone().into(), String::from("progress"), Closure::wrap(Box::new(progress_event.clone()) as Box<dyn FnMut(ProgressEvent)>)));
// p.push((reader.clone().into(), String::from("onloadend"), Closure::wrap(Box::new(progress_event.clone()) as Box<dyn FnMut(ProgressEvent)>)));
// ...snip...
if let Some(files) = files {
if let Some(file) = files.get(0) {
// ...snip...
add_event_listeners();
// ...snip...
}
}
});
let data_import_et: EventTarget = data_import_el.clone().into();
data_import_et
.add_event_listener_with_callback("change", data_import_cl.as_ref().unchecked_ref())
.expect("Failed: `unchecked_ref()`");
self.listeners
.push((data_import_et, String::from("change"), data_import_cl));
// ^^^^^^^^^^^^^^^
// THIS CLOSURE I CAN CAPTURE AND LATER DROP
self.add(p);
// ^^^^^^^^^^^^^^^
// THIS DOES NOT WORK
}
// helper function
fn data_import_process(_reader: &FileReader) {
// ...snip...
}
}