0

In Yew, I am attempting to bind a callback to the window resize event, triggering a Msg::Resize update. I have encountered: E0631 Type mismatch in closure arguments.`

Expanded version of the code can be found here:

This is the minimalist test case:

use gloo_console::log;
use gloo_events::EventListener;
use yew::prelude::*;

pub struct CanvasQuestion {}
pub enum Msg { Resize }

impl Component for CanvasQuestion {
    type Message = Msg;
    type Properties = ();

    fn create(_ctx: &Context<Self>) -> Self {
        Self { }
    }

    fn view(&self, _ctx: &Context<Self>) -> Html {
        html! { <canvas id="canvas"/> }
    }

    fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
        if first_render {
            // WORKS
            ctx.link().send_message(Msg::Resize);     
            
            // found signature of `fn(yew::Event) 
            // let on_window_resize = |_event: Event| {  // BROKEN
            let on_window_resize = |_event: &Event| {    // WORKS
                ctx.link().send_message(Msg::Resize);
            };

            // expected signature of `for<'r> fn(&'r yew::Event)
            EventListener::new( &web_sys::window().unwrap(),
                                "resize", on_window_resize );
        }
    }

    fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
        match msg {
            Msg::Resize => { true }  // rerender
        }
    }
}

Update

Replacing on_window_resize = |_event: Event| with on_window_resize = |_event: &Event| fixes the below errors

error[E0631]: type mismatch in closure arguments
   --> src/components/canvas_question.rs:68:43
    |
64  |  let on_window_resize = |_event: Event| {
    |                         --------------- found signature of `fn(yew::Event) -> _`
...
67  |  EventListener::new( &web_sys::window().unwrap(),
    |  ------------------ required by a bound introduced by this call
68  |  "resize", on_window_resize );
    |            ^^^^^^^^^^^^^^^^ expected signature of `for<'r> fn(&'r yew::Event) -> _`
    |
note: required by a bound in `EventListener::new`
   --> /home/jamie/.cargo/registry/src/github.com-1ecc6299db9ec823/gloo-events-0.1.2/src/lib.rs:338:12
    |
338 |  F: FnMut(&Event) + 'static,
    |     ^^^^^^^^^^^^^ required by this bound in `EventListener::new`

But exposes rust lifetime issues trying to access ctx.link() from inside the closure.

error[E0521]: borrowed data escapes outside of associated function
  --> src/components/fractal.rs:70:28
   |
51 |  fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
   |                         ---  - let's call the lifetime of this reference `'1`
   |                         |
   |                         `ctx` is a reference that is only valid in the associated function body
...
70 |               let listener = EventListener::new( &web_sys::window().unwrap(),
   |  ____________________________^
71 | |                                                "resize", on_window_resize );
   | |                                                                           ^
   | |                                                                           |
   | |___________________________________________________________________________`ctx` escapes the associated function body here
   |                                                                             argument requires that `'1` must outlive `'static`
  • Is there a way of defining a 'static lifetime for ctx: Context?
  • Is ctx.link() the best way to trigger internal message passing?
  • Would it be easier to call self.update(Msg::Resize) directly instead?
  • Does ctx.link() need to be stored as self.link in the CanvasQuestion struct?
  • What is the correct way to write and bind the on_window_resize() function?
Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
James McGuigan
  • 7,542
  • 4
  • 26
  • 29
  • 2
    Can you try `|_event: &Event|` (taking a reference to `Event`)? – Dogbert Jun 28 '22 at 17:51
  • @Dogbert Thank you for your response. Passing &Event as reference does indeed cure the original error messages. I have updated the question as this now exposes a rust lifetimes issue with accessing `ctx.link()` from inside the closure. – James McGuigan Jun 28 '22 at 21:47

1 Answers1

1

@Dogbert's comment of passing a reference to |_event: &Event| fixed error[E0631]: type mismatch in closure arguments, butr this turned out not to be needed in the final solution.

RTFM discovered the manual had the code pattern I needed

This uses the move keyword to pass ctx.link().callback() into the closure by value rather than reference, thus avoiding the lifetimes issue.

pub struct CanvasQuestion {
    listener: Option<EventListener>,
}
impl Component for CanvasQuestion {
    fn create(_ctx: &Context<Self>) -> Self {
        Self {
            listener: None,
        }
    }
    fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
        if first_render {
            ctx.link().send_message(Msg::Resize);
            let onresize = ctx.link().callback(|_: Event| Msg::Resize);
            let listener = EventListener::new(
                &web_sys::window().unwrap(),
                "resize",
                move |e| onresize.emit(e.clone())
            );
            self.listener = Some(listener);
        }
    }
}
James McGuigan
  • 7,542
  • 4
  • 26
  • 29