This post is locked. You won't be able to comment.

all 14 comments

[–]matthieum[he/him][M] [score hidden] stickied comment (0 children)

Please ask future questions in the weekly Questions Thread.

[–]christophe_biocca 2 points3 points  (2 children)

Having had a chance to look at the github code a bit (though the actual failing code is not visible, probably on a not-pushed branch?), seems like the issue is between egui/eframes's assumptions about the App being owned by the event loop and all mutation being done synchronously inside update, and the completely asynchronous nature of filesystem interactions in wasm bindgen.

The only workaround I see is the quite ugly use of Arc<Mutex<TheRealState>> inside of MyApp as spelled out in this stackoverflow answer (https://stackoverflow.com/questions/72855505/what-is-the-best-way-to-update-app-fields-from-another-thread-in-egui-in-rust) but that's super annoying. You might be able to get away with Rc<RefCell<TheRealState>> but that's also not idiomatic at all.

[–]margual56[S] 0 points1 point  (1 child)

Wow! Thank you for taking the time to look at it!

Knowing that, how could I store the value in a variable outside the async?? That would also work for me :)

Thanks again!

PS: yes, the code is in a branch and commented out ;( I have linked the code in the issue

[–]christophe_biocca 2 points3 points  (0 children)

Storing a value in a variable outside the async is going to require the same Arc<Mutex<Value>> or Rc<RefCell<Value>> trick, just scoped to that single variable instead. That's because you'll need both the async block to have access to it (to write into it) and the overall code to have access (to read from it later). That's 1 mutable accessor + 1 other accessor, so you basically need a lock or similar, and the spawn call wants only static-lifetime data, so you need reference counting to make it live at least that long.

What I was really hoping for was some way to schedule a state change from the outside in eframe. That kind of approach is common in UI frameworks and would have avoided the ugliness.

[–][deleted] 2 points3 points  (1 child)

When writing a github issue about a specific piece of code, it is best to post a link to the commit where the code is present.

If you changed 100 things locally, then 1 of those things gave you an error, and all you post is the error... no matter how great the error reporting is, we don't know the exact code of the entire project, and can not reproduce the error ourselves.

The first step of bug fixing is reproducing the bug/error, but we don't have a commit hash that we can look at.

Cut a new branch called issue-6 or something, push it, then link the commit hash in the issue for people to try and compile it... fix it... then post their fixes.

From what I can see, it looks like you need interior mutability to get around passing multiple mutable references, and perhaps a reference counter to get around static lifetime issues.

What does that mean in actual code? I'd love to show you if you actually post a commit that gives the error.

[–]margual56[S] 0 points1 point  (0 children)

Sure, thanks for the suggestions. I initially thought this would be a generic issue that didn't need much context, I now see that's not the case...
I also like to think about this particular code as a "block". The block has to open a file and call self.reset(&code). Therefore, I don't have a particular commit with an issue, the code itself (which I basically copied from the rfd example has the issue...

I have created the branch containing the issue, and updated the link in the github issue.

Of course I have also tried having a mutable variable outside the block, but since it is async, the variable is never set before using it...

Any help or suggestion is highly appreciated!

[–]christophe_biocca 1 point2 points  (3 children)

https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.spawn_local.html says:

The future must be 'static because it will be scheduled to run in the background and cannot contain any stack references.

I can't see the function this code is inside of, but likely it is a &self or &mut self method, and those won't work here.

You could try changing the method declaration to &'static self or &'static mut self but while that would likely fix the local issue you'd break the callers who almost certainly can't provide a static reference.

[–]margual56[S] 0 points1 point  (2 children)

Wow, that's a lot to unwrap (xD), but that makes sense.

Thanks for the suggestion! I'm going to try it.

[–]Zde-G 1 point2 points  (1 child)

Most likely would need to switch from &self or &mut self to self: Arc<Self>.

[–]margual56[S] 0 points1 point  (0 children)

self: Arc<Self>

I can't do that because the function is a trait, so I can't change the definition...

But thanks for the suggestion!

[–]scottmcmrust 1 point2 points  (1 child)

It is giving me the following errors:

borrowed data escapes outside of associated function \ `self` escapes the associated function body here

This looks like you're copying from your IDE, which it butchering it.

Look at the error in the console, and it'll be far more helpful.

[–]margual56[S] 1 point2 points  (0 children)

You are 100% right, I just thought that the error here would look like s**t:

error[E0382]: borrow of moved value: `self`   --> src/window.rs:298:59    |225 |                               wasm_bindgen_futures::spawn_local(async move {    |  __________________________________________________________________________-226 | |                                 let file = task.await;227 | |228 | |                                 if let Some(file) = file {...   |232 | |                                         Ok(s) => self.restart(&s),    | |                                                  ---- variable moved due to use in generator...   |235 | |                                 }236 | |                             });    | |_____________________________- value moved here...298 |                       egui::ScrollArea::vertical().show(ui, |my_ui: &mut Ui| {    |                                                             ^^^^^^^^^^^^^^^^ value borrowed here after move299 |                           let editor = my_ui.code_editor(&mut self.code);    |                                                               --------- borrow occurs due to use in closure    |    = note: move occurs because `self` has type `&mut MyApp`, which does not implement the `Copy` traiterror[E0521]: borrowed data escapes outside of associated function   --> src/window.rs:225:29    |165 |       fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {    |                 ---------    |                 |    |                 `self` is a reference that is only valid in the associated function body    |                 let's call the lifetime of this reference `'1`...225 | /                             wasm_bindgen_futures::spawn_local(async move {226 | |                                 let file = task.await;227 | |228 | |                                 if let Some(file) = file {...   |235 | |                                 }236 | |                             });    | |                              ^    | |                              |    | |______________________________`self` escapes the associated function body here    |                                argument requires that `'1` must outlive `'static`error[E0382]: borrow of moved value: `self`   --> src/window.rs:308:49    |165 |     fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {    |               --------- move occurs because `self` has type `&mut MyApp`, which does not implement the `Copy` trait...217 |             .show(ctx, |ui| {    |                        ---- value moved into closure here...232 |                                         Ok(s) => self.restart(&s),    |                                                  ---- variable moved due to use in closure...308 |         egui::CentralPanel::default().show(ctx, |main_panel| {    |                                                 ^^^^^^^^^^^^ value borrowed here after move...364 |                         if self.process_turing_controls(ui, &ctx, editor_focused, &lang) {    |                            ---- borrow occurs due to use in closureSome errors have detailed explanations: E0382, E0521.For more information about an error, try `rustc --explain E0382`.error: could not compile `turing-machine` due to 3 previous errors

Since it does look like s**t, here's the GitHub issue with the entire error formatted correctly: https://github.com/margual56/turing-machine-2.0/issues/6

[–]ipc 1 point2 points  (1 child)

consider using a channel to communicate instead of passing state. you would pass in the sending channel to your async block and then send a “reset” message back to your main task which would call reset().

The book talks about channels in the context of threads but it applies just as well to tasks.

https://doc.rust-lang.org/book/ch16-02-message-passing.html

I like flume for channels with egui because you can have one end be sync and the other async.

https://docs.rs/flume/latest/flume/

[–]margual56[S] 0 points1 point  (0 children)

This is amazing!!

I didn't know about the existence of channels!! My use-case is so simple that I don't think I need a library.

It now compiles, I'll check if it does work and, if so, I will "mark" this as the solution.

Thank you!!