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.)
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 Arc
s, 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)")
});
}