《改进JavaScript和Bust的互操作性:深入认识wasm-bindgen组件》由会员分享,可在线阅读,更多相关《改进JavaScript和Bust的互操作性:深入认识wasm-bindgen组件(9页珍藏版)》请在金锄头文库上搜索。
1、改进 JavaScript 和 Rust 的互操作性:深入认识 wasm-bindgen 组件最近我们已经见识了WebAssembly如何快速编译、加速JS库以及生成更小的二进制格式。我们甚至为Rust和JavaScript社区以及其他Web编程语言之间的更好的互操作性制定了高级规划。正如前面一篇文章中提到的,我想深入了解一个特定组件的细节,wasm-bindgen。今天WebAssembly标准只定义了四种类型:两种整数类型和两种浮点类型。然而,大多数情况下,JS和Rust开发人员正在使用更丰富的类型!例如,JS开发人员经常与互以添加或修改HTML节点相关的文档交互,而Rust开发人员使用类
2、似Result等类型进行错误处理,几乎所有程序员都使用字符串。被局限在仅使用由WebAssembly所提供的类型将会受到太多的限制,这就是wasm-bindgen出现的原因。wasm-bindgen的目标是提供一个JS和Rust类型之间的桥接。它允许JS使用字符串调用RustAPI,或Rust函数捕获JS异常。wasm-bindgen抹平了WebAssembly和JavaScript之间的阻抗失配,确保JavaScript可以高效地调用WebAssembly函数,并且无需boilerplate,同时WebAssembly可以对JavaScript函数执行相同的操作。wasm-bindgen项目
3、在其README文件中有更多描述。要入门,让我们深入到一个使用wasm-bindgen的例子中,然后探索它还有提供了什么。1、HelloWorld!学习新工具的最好也是最经典的方法之一就是探索下用它来输出“Hello,World!”。在这里,我们将探索一个这样的例子在页面里弹出“HelloWorld!”提醒框。这里的目标很简单,我们想要定义一个Rust的函数,给定一个名字,它会在页面上创建一个对话框,上面写着Hello,$name!在JavaScript中,我们可以将这个函数定义为:代码1.exportfunctiongreet(name)2.alert(Hello,$name!);3.不过在
4、这个例子里要注意的是,我们将把它用Rust编写。这里已经发生了很多我们必须要处理的事情:JavaScript将会调用一个WebAssembly模块,模块名是greetexport.Rust函数将一个字符串作为输入参数,也就是我们要打招呼的名字。在内部Rust会生成一个新的字符串,也就是传入的名字。最后Rust会调用JavaScript的alert函数,以刚创建的字符串作为参数。启动第一步,我们创建一个新的Rust工程:代码1.$cargonewwasm-greet-lib这将初始化一个新的wasm-greet文件夹,我们的工作都在这里面完成。接下来我们要使用如下信息修改我们的Cargo.tom
5、l(在Rust里相当于package.json):代码1.lib2.crate-type=cdylib3.4.dependencies5.wasm-bindgen=0.2我们先忽略lib节的内容,接下来的部分声明了对wasm-bindgen的依赖。这里的依赖包含了我们使用wasm-bindgen需要的所有的支持包。接下来,是时候编写一些代码了!我们使用下列内容替换了自动创建的srcb.rs:代码1.#!feature(proc_macro,wasm_custom_section,wasm_import_module)2.3.externcratewasm_bindgen;4.5.usewasm
6、_bindgen:prelude:*;6.7.#wasm_bindgen8.extern9.fnalert(s:&str);10.11.12.#wasm_bindgen13.pubfngreet(name:&str)14.alert(&format!(Hello,!,name);15.如果你不熟悉Rust,这可能看起来有点啰嗦,但不要害怕!随着时间的推移,wasm-bindgen项目不断改进,而且可以肯定的是,所有这些并不总是必要的。要注意的最重要的一点是#wasm_bindgen属性,这是一个在Rust代码中的注释,这里的意思是“请在必要时用wrapper处理这个”。我们对alert函数的导
7、入和greet函数的导出都被标注为这个属性。稍后,我们将看到在引擎盖下发生了什么。首先,我们从在浏览器中打开作为例子来切入正题!我们先编译wasm代码:代码1.$rustuptargetaddwasm32-unknown-unknown-toolchainnightly#onlyneededonce2.$cargo+nightlybuild-targetwasm32-unknown-unknown这段代码会生成一个wasm文件,路径为target/wasm32-unknown-unknown/debug/wasm_greet.wasm。如果我们使用工具如wasm2wat来看这个wasm文件里面
8、的内容,可能会有点吓人。结果发现这个wasm文件实际上还不能直接被JS调用!为了能让我们使用,我们需要执行一个或更多步骤:代码1.$cargoinstallwasm-bindgen-cli#onlyneededonce2.$wasm-bindgentarget/wasm32-unknown-unknown/debug/wasm_greet.wasm-out-dir.很多不可思议的事情发生都发生在这个步骤中:wasm-bindgenCLI工具对输入的wasm文件做后期处理,使它变的“suitable”可用。我们待会再来看“suitable”的意思,现在我们可以肯定的说,如果我们引入刚创建的was
9、m_greet.js文件(wasm-bindgen工具创建的),我们已经获取到了在Rust中定义的greet函数。最终我们接下来要做的是使用bundler对其打包,然后创建一个HTML页面运行我们的代码。在写这篇文章的时候,只有Webpacks4.0release对WebAssembly的使用有足够的支持(尽管暂时已经有了Chromecaveat)。总有一天,更多的bundler也会接着支持WebAssmbly。在这我不再描述细节,但是你可以看一下在Github仓库里的example配置。不过如果我们看内容,这个页面中我们的JS在看起来是这样的:代码1.construst=import(./w
10、asm_greet);2.rust.then(m=m.greet(World!);就是这些了!现在打开我们的网页就会显示一个不错的“Hello,World!”对话框,这就是Rust驱动的。2、wasm-bindgen是如何工作的唷,那是一个巨大的“Hello,World!”。让我们深入了解一下更多的细节,以了解后台发生了什么以及该工具是如何工作的。wasm-bindgen最重要的方面之一就是它的集成基本上是建立在一个概念之上的,即一个wasm模块仅是另一种ES模块。例如,在上述中我们想要一个带有如下签名的ES模块(在Typescript中):代码1.exportfunctiongreet(s:
11、string);WebAssembly无法在本地执行此操作(请记住,它目前只支持数字),所以我们依靠wasm-bindgen来填补空白。在上述的最后一步中,当我们运行wasm-bindgen工具时,你会注意到wasm_greet.js文件与wasm_greet_bg.wasm文件一起出现。前者是我们想要的实际JS接口,执行任何必要的处理以调用Rust。*_bg.wasm文件包含实际的实现和我们所有的编译后的代码。我们可以通过引入./wasm_greet模块得到Rust代码愿意暴露出来的东西。我们已经看到了是如何集成的,可以继续看看执行的结果如何。首先是我们的示例:代码1.construst=i
12、mport(./wasm_greet);2.rust.then(m=m.greet(World!);我们在这里以异步的方式导入接口,等待导入完成(下载和编译wasm)。然后调用模块的greet函数。www.f-1.cc注:这里用到的异步加载目前需要Webpack来实现,但总会不需要的。而且,其它打包工具可能没有此功能。如果我们看看由wasm-bindgen工具为wasm_greet.js文件生成的内容,会看到像这样的代码:代码1.import*aswasmfrom./wasm_greet_bg;2.3./.4.5.exportfunctiongreet(arg0)6.constptr0,len
13、0=passStringToWasm(arg0);7.try8.constret=wasm.greet(ptr0,len0);9.returnret;10.finally11.wasm._wbindgen_free(ptr0,len0);12.13.14.15.exportfunction_wbg_f_alert_alert_n(ptr0,len0)16./.17.注:记住这是生成的,未经优化的代码,它可能既不优雅也不简洁!在Rust中通过LTO(LinkTimeOptimization,连接时优化)创建新的发行版,再通过JS打包工具流程(压缩)之后,可能会精简一些。现在可以了解如何使用was
14、m-bindgen来生成greet函数。在底层它仍然调用wasm的greet函数,但是它是用一个指针和长度来调用的而不是用字符串。了解passStringToWasm的更多细节可以访问LinClarkspreviouspost。它包含了所有的模板,对我们来说这是除了wasm-bindgen工具以外还需要去写的东西!然后我们接下来看_wbg_f_alert_alert_n函数。进入更深一层,下一个我们感兴趣的就是WebAssmbly中的greet函数。为了了解这个,我们先来看Rust编译器能访问到的代码。注意像上面生成的这种JSwrapper,在这里你不用写greet的导出符号,#wasm_bindgen属性会生成一个shim,由它来为你翻译,命名如下:代码1.pubfngreet(name:&str)2.alert(&forma