如何将Rust与wasm-bindgen结合使用,以创建一个具有状态的另一个闭包?

如何解决如何将Rust与wasm-bindgen结合使用,以创建一个具有状态的另一个闭包?

我正在尝试创建一个小型Web应用程序,该应用程序将允许用户将文件拖放到窗口上。然后将读取文件,并将其内容和文件名一起打印到控制台。另外,这些文件将被添加到列表中。

JS中的等效代码可能类似于:

window.ondragenter = (e) => {
    e.preventDefault();
}
window.ondragover = (e) => {
    e.preventDefault();
}

const allFiles = [];

const dropCallback = (e) => {
  e.preventDefault();
  const files = e.dataTransfer.files;
  console.log("Got",files.length,"files");
  for (let i = 0; i < files.length; i++) {
    const file = files.item(i);
    const fileName = file.name;
    const readCallback = (text) => {
      console.log(fileName,text);
      allFiles.push({fileName,text});
    }
    file.text().then(readCallback);
  }
};

window.ondrop = dropCallback;

当尝试在Rust中执行此操作时,我遇到一个问题,即外部闭包需要实现FnOnce才能再次将all_files移出其作用域,这会破坏{{ 1}}。 Closure::wrap不会解决问题,因为我需要能够将多个文件拖放到窗口上。

这是我没有运气尝试过的代码:

Closure::once

我得到的错误是

use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen::JsValue;

macro_rules! console_log {
    ($($t:tt)*) => (web_sys::console::log_1(&JsValue::from(format_args!($($t)*).to_string())))
}

struct File {
    name: String,contents: String,}

#[wasm_bindgen]
pub fn main() {
    let mut all_files = Vec::new();

    let drop_callback = Closure::wrap(Box::new(move |event: &web_sys::Event| {
        event.prevent_default();
        let drag_event_ref: &web_sys::DragEvent = JsCast::unchecked_from_js_ref(event);
        let drag_event = drag_event_ref.clone();
        match drag_event.data_transfer() {
            None => {}
            Some(data_transfer) => match data_transfer.files() {
                None => {}
                Some(files) => {
                    console_log!("Got {:?} files",files.length());
                    for i in 0..files.length() {
                        if let Some(file) = files.item(i) {
                            let name = file.name();
                            let read_callback = Closure::wrap(Box::new(move |text: JsValue| {
                                let contents = text.as_string().unwrap();
                                console_log!("Contents of {:?} are {:?}",name,contents);

                                all_files.push(File {
                                    name,contents
                                });
                            }) as Box<dyn FnMut(JsValue)>);

                            file.text().then(&read_callback);

                            read_callback.forget();
                        }
                    }
                }
            },}
    }) as Box<dyn FnMut(&web_sys::Event)>);

    // These are just necessary to make sure the drop event is sent
    let drag_enter = Closure::wrap(Box::new(|event: &web_sys::Event| {
        event.prevent_default();
        console_log!("Drag enter!");
    }) as Box<dyn FnMut(&web_sys::Event)>);

    let drag_over = Closure::wrap(Box::new(|event: &web_sys::Event| {
        event.prevent_default();
        console_log!("Drag over!");
    }) as Box<dyn FnMut(&web_sys::Event)>);

    // Register all the events on the window
    web_sys::window()
        .and_then(|win| {
            win.set_ondragenter(Some(JsCast::unchecked_from_js_ref(drag_enter.as_ref())));
            win.set_ondragover(Some(JsCast::unchecked_from_js_ref(drag_over.as_ref())));
            win.set_ondrop(Some(JsCast::unchecked_from_js_ref(drop_callback.as_ref())));
            win.document()
        })
        .expect("Could not find window");

    // Make sure our closures outlive this function
    drag_enter.forget();
    drag_over.forget();
    drop_callback.forget();
}

在一个我无法以更简单的形式复制的更复杂的示例中,我得到了一个更加神秘的错误,但我希望它与以上内容有关:

error[E0525]: expected a closure that implements the `FnMut` trait,but this closure only implements `FnOnce`
  --> src/lib.rs:33:72
   |
33 |   ...                   let read_callback = Closure::wrap(Box::new(move |text: JsValue| {
   |                                                           -        ^^^^^^^^^^^^^^^^^^^^ this closure implements `FnOnce`,not `FnMut`
   |  _________________________________________________________|
   | |
34 | | ...                       let contents = text.as_string().unwrap();
35 | | ...                       console_log!("Contents of {:?} are {:?}",contents);
36 | | ...
37 | | ...                       all_files.push(File {
38 | | ...                           name,| |                               ---- closure is `FnOnce` because it moves the variable `name` out of its environment
39 | | ...                           contents
40 | | ...                       });
41 | | ...                   }) as Box<dyn FnMut(JsValue)>);
   | |________________________- the requirement to implement `FnMut` derives from here

error[E0525]: expected a closure that implements the `FnMut` trait,but this closure only implements `FnOnce`
  --> src/lib.rs:20:48
   |
20 |       let drop_callback = Closure::wrap(Box::new(move |event: &web_sys::Event| {
   |                                         -        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this closure implements `FnOnce`,not `FnMut`
   |  _______________________________________|
   | |
21 | |         event.prevent_default();
22 | |         let drag_event_ref: &web_sys::DragEvent = JsCast::unchecked_from_js_ref(event);
23 | |         let drag_event = drag_event_ref.clone();
...  |
33 | |                             let read_callback = Closure::wrap(Box::new(move |text: JsValue| {
   | |                                                                        -------------------- closure is `FnOnce` because it moves the variable `all_files` out of its environment
...  |
50 | |         }
51 | |     }) as Box<dyn FnMut(&web_sys::Event)>);
   | |______- the requirement to implement `FnMut` derives from here

error: aborting due to 2 previous errors; 1 warning emitted

For more information about this error,try `rustc --explain E0525`.
error: could not compile `hello_world`.

To learn more,run the command again with --verbose.

我尝试将error[E0277]: expected a `std::ops::FnMut<(&web_sys::Event,)>` closure,found `[closure@src/main.rs:621:52: 649:10 contents:std::option::Option<std::string::String>,drop_proxy:winit::event_loop::EventLoopProxy<CustomEvent>]` --> src/main.rs:621:43 | 621 | let drop_callback = Closure::wrap(Box::new(move |event: &web_sys::Event| { | ___________________________________________^ 622 | | event.prevent_default(); 623 | | let drag_event_ref: &web_sys::DragEvent = JsCast::unchecked_from_js_ref(event); 624 | | let drag_event = drag_event_ref.clone(); ... | 648 | | } 649 | | }) as Box<dyn FnMut(&web_sys::Event)>); | |__________^ expected an `FnMut<(&web_sys::Event,drop_proxy:winit::event_loop::EventLoopProxy<CustomEvent>]` 变量放入all_files中,但是仍然出现类似的错误。在Rust中有什么技巧或类型可以用来解决此问题并实现我想要的吗?

解决方法

首先,您尝试将name复制到File的多个实例中,但是必须将其克隆。其次,您需要适当地确保all_files在闭包想要调用它时将可用。一种方法是使用RefCell启用多个闭包对其进行写入,并将其包装在Rc中以确保只要任何闭包都处于活动状态,它就保持活动状态。 / p>

尝试一下:

use std::{cell::RefCell,rc::Rc};
use wasm_bindgen::{prelude::*,JsCast,JsValue};

macro_rules! console_log {
    ($($t:tt)*) => (web_sys::console::log_1(&JsValue::from(format_args!($($t)*).to_string())))
}

struct File {
    name: String,contents: String,}

#[wasm_bindgen]
pub fn main() {
    let all_files = Rc::new(RefCell::new(Vec::new()));

    let drop_callback = Closure::wrap(Box::new(move |event: &web_sys::Event| {
        event.prevent_default();
        let drag_event_ref: &web_sys::DragEvent = event.unchecked_ref();
        let drag_event = drag_event_ref.clone();
        match drag_event.data_transfer() {
            None => {}
            Some(data_transfer) => match data_transfer.files() {
                None => {}
                Some(files) => {
                    console_log!("Got {:?} files",files.length());
                    for i in 0..files.length() {
                        if let Some(file) = files.item(i) {
                            let name = file.name();
                            let all_files_ref = Rc::clone(&all_files);
                            let read_callback = Closure::wrap(Box::new(move |text: JsValue| {
                                let contents = text.as_string().unwrap();
                                console_log!("Contents of {:?} are {:?}",&name,contents);

                                (*all_files_ref).borrow_mut().push(File {
                                    name: name.clone(),contents,});
                            })
                                as Box<dyn FnMut(JsValue)>);

                            file.text().then(&read_callback);

                            read_callback.forget();
                        }
                    }
                }
            },}
    }) as Box<dyn FnMut(&web_sys::Event)>);

    // These are just necessary to make sure the drop event is sent
    let drag_enter = Closure::wrap(Box::new(|event: &web_sys::Event| {
        event.prevent_default();
        console_log!("Drag enter!");
    }) as Box<dyn FnMut(&web_sys::Event)>);

    let drag_over = Closure::wrap(Box::new(|event: &web_sys::Event| {
        event.prevent_default();
        console_log!("Drag over!");
    }) as Box<dyn FnMut(&web_sys::Event)>);

    // Register all the events on the window
    web_sys::window()
        .and_then(|win| {
            win.set_ondragenter(Some(drag_enter.as_ref().unchecked_ref()));
            win.set_ondragover(Some(drag_over.as_ref().unchecked_ref()));
            win.set_ondrop(Some(drop_callback.as_ref().unchecked_ref()));
            win.document()
        })
        .expect("Could not find window");

    // Make sure our closures outlive this function
    drag_enter.forget();
    drag_over.forget();
    drop_callback.forget();
}

请注意,如果您正在使用多个线程,则可能需要除RefCell之外的其他内容(可能是Mutex)。另外,我还将JsCast::unchecked_from_js_ref(x)的用法更改为更规范的x.as_ref().unchecked_ref()

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-