如何将闭包转换为 js_sys::Function?

问题描述 投票:0回答:3

如何将本地闭包转换为

js_sys::Function

我想做这样的事情:

let canvas = document.get_element_by_id("canvas").unwrap();
let e: web_sys::HtmlElement = canvas.dyn_into().unwrap();
let f = || {};
e.set_onresize(Some(&f.into()));
rust webassembly wasm-bindgen
3个回答
9
投票

我找到了解决方案在这里

let f = Closure::wrap(Box::new(move || { /* whatever */}) as Box<dyn FnMut()>);
e.set_onresize(Some(f.as_ref().unchecked_ref()));
f.forget(); // It is not good practice, just for simplification!

0
投票

答案也可以在

wasm_bindgen::closure::Closure
页面上找到。

use wasm_bindgen::{closure::Closure, JsCast};

let cb = Closure::new(|| { ... });
let cb = cb.as_ref().unchecked_ref();

0
投票

以下将闭包设置为回调的方法比Hossein Noroozpour 的回答中所示的方法略短。它改编自这个example

let closure: Closure<dyn Fn()> = Closure::new(move || {
      // Do something here
});
your_html_element.set_oninput(Some(closure.as_ref().unchecked_ref()));
closure.forget();

这里有一个更完整的示例,展示了如何在 file picker 上设置回调。回调访问一个 JavaScript worker:

let document = window().unwrap().document().unwrap();
let worker = Worker::new("./worker.js").unwrap();

let file_picker_elem_id = "file_picker";
match get_input_element(file_picker_elem_id, &document) {
    Some(file_picker) => {
        // We need "move" to access the worker inside the closure
        let closure: Closure<dyn Fn()> = Closure::new(move || {
            log_to_browser(format!(
                "The file picker callback. Worker: {worker:?}"
            ));
        });
        file_picker.set_oninput(Some(closure.as_ref().unchecked_ref()));
        closure.forget();
    }
    None => log_to_browser(format!(
        "Couldn't get file picker. Used \"{file_picker_elem_id}\" as its ID"
    )),
}

/// Gets an [[HtmlInputElement]] by its `elem_id`.
fn get_input_element(elem_id: &str, document: &Document) -> Option<HtmlInputElement> {
    document
        .get_element_by_id(elem_id)
        .and_then(|elem| elem.dyn_ref::<HtmlInputElement>().map(ToOwned::to_owned))
}
fn log_to_browser(log_msg: String) {
    console::log_1(&log_msg.into());
}

我试着简化闭包到

js_sys::Function
s 的转换:

pub fn to_js_func<F>(closure: F) -> Option<&'static js_sys::Function>
where
    F: IntoWasmClosure<dyn Fn()> + 'static,
{
    let big_closure: Closure<dyn Fn()> = Closure::new(closure);

    let func: &js_sys::Function = big_closure.as_ref().unchecked_ref();
    big_closure.forget();
    Some(func)
}

但这不起作用,因为函数拥有创建的闭包。此外,要使用

js_sys::Function
类型,我需要将
js-sys
依赖项添加到 Cargo.toml。如果有人知道解决这个问题的方法,请告诉我们。


一个不太通用的辅助函数:

fn set_on_input<F>(input_elem: &HtmlInputElement, closure: F)
where
    F: IntoWasmClosure<dyn Fn()> + 'static,
{
    let big_closure: Closure<dyn Fn()> = Closure::new(closure);
    input_elem.set_oninput(Some(big_closure.as_ref().unchecked_ref()));
    big_closure.forget();
}

这允许将 Rust 闭包设置为 HTML 元素上的回调:

let closure = move || {
    log_to_browser(format!(
        "The file picker callback. Worker: {worker:?}"
    ));
};
set_on_input(&file_picker, closure);

除了闭包,你还可以设置一个函数作为回调:

set_on_input(&file_picker, test_callback);

其中

test_callback
只是一个普通的 Rust 函数:

fn test_callback() {
    log_to_browser("test_callback 🥳".into());
}
© www.soinside.com 2019 - 2024. All rights reserved.