James McGuigan
James McGuigan

Reputation: 8086

Type mismatch in closure arguments + closure lifetimes

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`

Upvotes: 0

Views: 206

Answers (1)

James McGuigan
James McGuigan

Reputation: 8086

@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);
        }
    }
}

Upvotes: 1

Related Questions