I'm trying to extend the example dom in wasm-bindgen to encapsulate some of the logic, and handle callbacks, but I'm getting a lifetime error:
The code
//! A simple example of wrapping the raw DOM calls to make it more rust-y.
extern crate wasm_bindgen;
extern crate web_sys;
use wasm_bindgen::prelude::*;
// Called by our JS entry point to run the example
#[wasm_bindgen]
pub fn run() -> Result<(), JsValue> {
let el = Element::new("p")?;
el.set_inner_html("Hello from Rust!");
el.append_to_body()?;
Ok(())
}
#[derive(Debug, Copy, Clone)]
pub struct Element(&'static web_sys::Element);
impl Element {
pub fn new(tag_name: &str) -> Result<Self, JsValue> {
// these methods should never fail.
let document = web_sys::window().unwrap().document().unwrap();
let el = document.create_element(tag_name)?;
Ok(Element(&el))
}
pub fn set_inner_html(&self, content: &str) {
self.0.set_inner_html(content)
}
pub fn append_to_body(&self) -> Result<(), JsValue> {
let body = web_sys::window().unwrap().document().unwrap().body().unwrap();
AsRef::<web_sys::Node>::as_ref(&body).append_child(self.0.as_ref())?;
Ok(())
}
pub fn set_on_click<F>(&self, callback: F) where F: Fn(Element) {
let closure = Closure::wrap(Box::new(move || {
callback(self.clone())
}) as Box<Fn()>);
}
}
I'm confused as to why I'm getting a lifetime error, because my Element is Copy, and has no lifetime.
//! A simple example of wrapping the raw DOM calls to make it more rust-y.
extern crate wasm_bindgen;
extern crate web_sys;
use wasm_bindgen::prelude::*;
// Called by our JS entry point to run the example
#[wasm_bindgen]
pub fn run() -> Result<(), JsValue> {
let el = Element::new("p")?;
el.set_inner_html("Hello from Rust!");
el.append_to_body()?;
Ok(())
}
#[derive(Debug, Clone)]
pub struct Element(web_sys::Element);
impl Element {
pub fn new(tag_name: &str) -> Result<Self, JsValue> {
// these methods should never fail.
let document = web_sys::window().unwrap().document().unwrap();
let el = document.create_element(tag_name)?;
Ok(Element(el))
}
pub fn set_inner_html(&self, content: &str) {
self.0.set_inner_html(content)
}
pub fn append_to_body(&self) -> Result<(), JsValue> {
let body = web_sys::window().unwrap().document().unwrap().body().unwrap();
AsRef::<web_sys::Node>::as_ref(&body).append_child(self.0.as_ref())?;
Ok(())
}
pub fn set_on_click<F>(&self, callback: F) where F: Fn(Element) {
let closure = Closure::wrap(Box::new(move || {
callback(self.clone())
}) as Box<Fn()>);
}
}
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> examples/dom-wrapper/src/lib.rs:39:46
|
39 | let closure = Closure::wrap(Box::new(move || {
| ______________________________________________^
40 | | callback(self.clone())
41 | | }) as Box<Fn()>);
| |_________^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 38:5...
--> examples/dom-wrapper/src/lib.rs:38:5
|
38 | / pub fn set_on_click<F>(&self, callback: F) where F: Fn(Element) {
39 | | let closure = Closure::wrap(Box::new(move || {
40 | | callback(self.clone())
41 | | }) as Box<Fn()>);
42 | | }
| |_____^
= note: ...so that the types are compatible:
expected &Element
found &Element
= note: but, the lifetime must be valid for the static lifetime...
= note: ...so that the types are compatible:
expected wasm_bindgen::closure::WasmClosure
found wasm_bindgen::closure::WasmClosure
Thanks! This solved the issue. I now have a different error message:
New error
error[E0277]: expected a `std::ops::Fn<()>` closure, found `[closure@examples/dom-wrapper/src/lib.rs:41:46: 43:10 callback:std::boxed::Box<dyn std::ops::Fn(Element)>, self2:Element]`
--> examples/dom-wrapper/src/lib.rs:41:37
|
41 | let closure = Closure::wrap(Box::new(move || {
| _____________________________________^
42 | | (*callback)(self2)
43 | | }) as Box<Fn()>);
| |__________^ expected an `Fn<()>` closure, found `[closure@examples/dom-wrapper/src/lib.rs:41:46: 43:10 callback:std::boxed::Box<dyn std::ops::Fn(Element)>, self2:Element]`
|
= help: the trait `std::ops::Fn<()>` is not implemented for `[closure@examples/dom-wrapper/src/lib.rs:41:46: 43:10 callback:std::boxed::Box<dyn std::ops::Fn(Element)>, self2:Element]`
= note: wrap the `[closure@examples/dom-wrapper/src/lib.rs:41:46: 43:10 callback:std::boxed::Box<dyn std::ops::Fn(Element)>, self2:Element]` in a closure with no arguments: `|| { /* code */ }
= note: required for the cast to the object type `dyn std::ops::Fn()
Updated code
//! A simple example of wrapping the raw DOM calls to make it more rust-y.
extern crate wasm_bindgen;
extern crate web_sys;
use wasm_bindgen::prelude::*;
// Called by our JS entry point to run the example
#[wasm_bindgen]
pub fn run() -> Result<(), JsValue> {
let el = Element::new("p")?;
el.set_inner_html("Hello from Rust!");
el.append_to_body()?;
Ok(())
}
#[derive(Debug, Clone)]
pub struct Element(web_sys::Element);
impl Element {
pub fn new(tag_name: &str) -> Result<Self, JsValue> {
// these methods should never fail.
let document = web_sys::window().unwrap().document().unwrap();
let el = document.create_element(tag_name)?;
Ok(Element(el))
}
pub fn set_inner_html(&self, content: &str) {
self.0.set_inner_html(content)
}
pub fn append_to_body(&self) -> Result<(), JsValue> {
let body = web_sys::window().unwrap().document().unwrap().body().unwrap();
AsRef::<web_sys::Node>::as_ref(&body).append_child(self.0.as_ref())?;
Ok(())
}
pub fn set_on_click<F>(&self, callback: F) where F: Fn(Element) {
let self2 = self.clone();
let closure = Closure::wrap(Box::new(move || {
callback(self2)
}) as Box<Fn()>);
}
}
Now I'm confused because the advice it give me (about wrapping the closure) I've already done!
I think this is because your closure can't implement Fn() because F (i.e. callback) consumes the Element you give it, and an Fn must be callable many times. So, you can either make F: Fn(&Element) and pass it a &self2 or make the closure do callback(self2.clone()).