0

I am using wry to spawn a few web views. Each view has a on_page_load_handler. Inside the handler I need to access the web view to e.g. navigate to another website. Sadly I fail to pass the web view to the on_page_load_handler. I think I understand the problem, yet I fail to find a solution. This is more of a Rust problem than wry in particular.

So how can I gain access to the web view inside the handler?

This is the code I am trying to run:

use tao::{
    event::{Event, WindowEvent},
    event_loop::{ControlFlow, EventLoop},
    window::WindowBuilder
};
use wry::{
    PageLoadEvent,
    WebView,
    WebViewBuilder
};

fn main() {
    let event_loop = EventLoop::new();
    let window = WindowBuilder::new()
        .build(&event_loop)
        .expect("Failed to create window.");

    let mut views: Vec<WebView> = Vec::new();
    for i in 0..3 {
        views.push(WebViewBuilder::new_as_child(&window)
        .with_url("https://stackoverflow.com/")
        .with_on_page_load_handler(move |event, target_url| {
            views[i].load_url("https://chatgpt.com/"); // <-- Problem here
        }).build()
        .expect("Failed to create web view"));
    }

    event_loop.run(move |event, _, control_flow| {
        *control_flow = ControlFlow::Wait;
    
        match event {
            Event::WindowEvent {
                event: WindowEvent::CloseRequested,
                ..
            } => *control_flow = ControlFlow::Exit,
            _ => ()
        }
    });
}

Rust error:

error[E0382]: borrow of moved value: `views`
  --> src/main.rs:19:3
   |
17 |     let mut views: Vec<WebView> = Vec::new();
   |         --------- move occurs because `views` has type `Vec<WebView>`, which does not implement the `Copy` trait
18 |     for i in 0..3 {
   |     ------------- inside of this loop
19 |         views.push(WebViewBuilder::new_as_child(&window)
   |         ^^^^^ value borrowed here after move
20 |         .with_url("https://stackoverflow.com/")
21 |         .with_on_page_load_handler(move |event, target_url| {
   |                                    ------------------------ value moved into closure here, in previous iteration of loop

error[E0505]: cannot move out of `views` because it is borrowed
  --> src/main.rs:21:30
   |
17 |     let mut views: Vec<WebView> = Vec::new();
   |         --------- binding `views` declared here
18 |     for i in 0..3 {
19 |         views.push(WebViewBuilder::new_as_child(&window)
   |         ----- ---- borrow later used by call
   |         |
   |         borrow of `views` occurs here
20 |         .with_url("https://stackoverflow.com/")
21 |         .with_on_page_load_handler(move |event, target_url| {
   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^ move out of `views` occurs here
22 |             views[i].load_url("https://chatgpt.com/");
   |             ----- move occurs due to use in closure

Signature of with_on_page_load_handler:

pub fn with_on_page_load_handler(
  mut self,
  handler: impl Fn(PageLoadEvent, String) + 'static,
) -> Self

The arguments to handler aren't useful for getting access to the built WebView, with neither the PageLoadEvent nor String providing access to it.
Thank you in advance! (I dont care about the rules. I appreciate each and every single one of you for taking the time to look at this.)

6
  • Wouldn't this repeatedly load the same page over and over again?
    – drewtato
    Commented Jun 29 at 20:09
  • @drewtato yes it would. this is just a minimal example. the issue is that i cannot access views[i] inside the closure. it has something to do with ownership i believe, but i have no idea how one would fix this the "Rust way". Commented Jun 29 at 23:29
  • 1
    I think it'll be something like this: play.rust-lang.org/… It might be easier if you had some controller outside all this and passed messages between it and the closure.
    – drewtato
    Commented Jun 30 at 0:28
  • 1
    OHHH, I see what went wrong. The vec! macro clones the item you give it, which means all these Arcs are the same Arc. Try this: play.rust-lang.org/… I also fixed the problem with expect.
    – drewtato
    Commented Jun 30 at 18:15
  • 1
    @drewtato you actually did it! Thank you very much. I learned a few things here. If you post this as an answer I will mark this as solved. Commented Jun 30 at 18:42

1 Answer 1

1

There are two problems you need to solve. The first is that the values in the Vec are self-referential. I've solved this by storing the values in the Arcs, and then moving a Weak reference into the closure. The second is that the closure needs to refer to something that doesn't exist until after the closure is created. This is solved using OnceLock, which allows setting its contents after being created and without needing an exclusive reference (&mut).

use std::sync::{Arc, OnceLock};

// Using `vec![v; N]` clones `v`, so we can't use it here since each
// `Arc` needs to be separate.
let views: Vec<Arc<OnceLock<WebView>>> = (0..3).map(|_| Arc::new(OnceLock::new())).collect();
for view_arc in &views {
    let view_weak = Arc::downgrade(view_arc);
    let view: WebView = WebViewBuilder::new_as_child(&window)
        .with_url("https://stackoverflow.com/")
        .with_on_page_load_handler(move |event, target_url| {
            view_weak
                .upgrade()
                .expect("view has been dropped")
                .get()
                .expect("view was not initialized (this shouldn't happen)")
                .load_url("https://chatgpt.com/")
                .expect("failed loading url");
        })
        .build()
        .expect("Failed to create web view");

    // `expect` doesn't work when the error type doesn't impl `Debug`, so
    // instead this uses a plain `panic!`.
    view_arc.set(view).unwrap_or_else(|_| {
        panic!("something else initialized the cell (this shouldn't happen)")
    });
}
1
  • 2
    I also just noticed wry doesn't need this to be Send, so you can use Vec<Rc<OnceCell<WebView>>> instead, but the performance difference between the two should be extremely small, and the rest should be basically identical.
    – drewtato
    Commented Jun 30 at 20:01

Not the answer you're looking for? Browse other questions tagged or ask your own question.