pk10免费滚雪球计划 北京pk赛车人工计划 一分钟赛车是官方的吗 北京pk10数学天才揭秘 北京pk10网赌害人案例 北京pk10保罗软件计划 pk10模式长期稳赚6码 北京pk10公式大全 网络赛车赌博是骗局吗 北京pk赛车是不是真的 北京pk10八码公式教程 一无所有怎么白手起家 北京pk10正规平台平台 北京pk赛车历史开结果 pk10买8码杀2码好方法 北京pk10冠亚和值漏洞 正规北京pk赛车彩票 玩北京pk10输钱经历 北京pk10专家在线计划 北京pk赛车正规吗 北京pk10为什么不禁止 北京pk拾赛车有人赢吗 北京pk10代理违法吗 pk10挂机模式真的稳赢 pk10冠军固定公式软件 北京pk10如何看大小 北京pk哈赛车官方网站 北京pk10六码公式教程 pk10去一尾算法举例 北京pk10冠军5码方法
阅读更多
引用
原文:JavaScript to Rust and Back Again: A wasm-bindgen Tale
链接:https://hacks.mozilla.org/2018/04/javascript-to-rust-and-back-again-a-wasm-bindgen-tale/
译者:Tocy, 琪花亿草, 雪落无痕xdj, 边城

最近我们已经见识了WebAssembly如何快速编译、加速JS库以及生成更小的二进制格式。我们甚至为Rust和JavaScript社区以及其他Web编程语言之间的更好的互操作性制定了高级规划。正如前面一篇文章中提到的,我想深入了解一个特定组件的细节,wasm-bindgen。

今天WebAssembly标准只定义了四种类型:两种整数类型和两种浮点类型。然而,大多数情况下,JS和Rust开发人员正在使用更丰富的类型! 例如,JS开发人员经常与互以添加或修改HTML节点相关的文?#21040;?#20114;,而Rust开发人员使用类似Result等类型进行错误处理,几乎所有程序员都使用字符串。


被局限在仅使用由WebAssembly所提供的类型将会受到太多的限制,这就是wasm-bindgen出现的原因。


wasm-bindgen的目标是提供一个JS和Rust类型之间的桥接。它允许JS使用字符串调用Rust API,或Rust函数捕获JS异常。wasm-bindgen抹平了WebAssembly和JavaScript之间的阻抗失配,确保JavaScript可以高效地调用WebAssembly函数,并且无需boilerplate,同时WebAssembly可以对JavaScript函数执行相同的操作。

wasm-bindgen项目在其README文件中有更多描述。要入门,让我们深入到一个使用wasm-bindgen的例子中,然后探索它还有提供了什么。

1、Hello World!

学习新工具的最好也是最经典的方法之一就是探索下用它来输出“Hello, World!”。在这里,我们将探索一个这样的例子——在页面里弹出“Hello World!”提醒框。

这里的目标很简单,我们想要定义一个Rust的函数,给定一个名字,它会在页面上创建一个对话框,上面写着Hello,$name!在JavaScript中,我们可以将这个函数定义为:
export function greet(name) {
    alert(`Hello, ${name}!`);
}

?#36824;?#22312;这个例子里要注意的是,我们将把它用Rust编写。这里已经发生了很多我们必须要处理的事情:
  • JavaScript将会调用一个WebAssembly 模块, 模块名是 greetexport.
  • Rust函数将一个字符串作为输入?#38382;?#20063;就是我们要打招呼的名字。
  • 在内部Rust会生成一个新的字符串,也就是传入的名字。
  • 最后Rust会调用JavaScript的 alert函数,以刚创建的字符串作为?#38382;?/ul>
  • 启动第一步,我们创建一个新的Rust工程:
    $ cargo new wasm-greet --lib

    这将初始化一个新的wasm-greet文件夹,我们的工作都在这里面完成。接下来我们要使用如下信息修改我们的Cargo.toml(在Rust里相当于package.json):
    [lib]
    crate-type = ["cdylib"]
    
    [dependencies]
    wasm-bindgen = "0.2"

    我们先忽略[lib]节的内容,接下来的部分声明了对wasm-bindgen的?#35272;怠?#36825;里的?#35272;?#21253;含了我们使用wasm-bindgen需要的所有的支持包。

    接下来,是时候编写一些代码了!我们使用下列内容替换了自动创建的src/lib.rs:
    #![feature(proc_macro, wasm_custom_section, wasm_import_module)]
    
    extern crate wasm_bindgen;
    
    use wasm_bindgen::prelude::*;
    
    #[wasm_bindgen]
    extern {
        fn alert(s: &str);
    }
    
    #[wasm_bindgen]
    pub fn greet(name: &str) {
        alert(&format!("Hello, {}!", name));
    }

    如果你不熟悉Rust,这可能看起来有点啰嗦,但不要害怕!随着时间的推移,wasm-bindgen项目不断改进,而且可以肯定的是,所有这些并不总是必要的。

    要注意的最重要的一点是#[wasm_bindgen]属性,这是一个在Rust代码中的注释,这里的意思是“请在必要时用wrapper处理这个”。我们对alert函数的导入和greet函数的导出都被标注为这个属性。稍后,我们将看到在引擎盖下发生了什么。

    首先,我们从在浏览器中打开作为例子来切入正题!我们先编译wasm代码:
    $ rustup target add wasm32-unknown-unknown --toolchain nightly # only needed once
    $ cargo +nightly build --target wasm32-unknown-unknown

    这段代码会生成一个wasm文件,路径为target/wasm32-unknown-unknown/debug/wasm_greet.wasm。如果我们使用工具如wasm2wat来看这个wasm文件里面的内容,可能会有点吓人。

    结果发现这个wasm文件?#23548;?#19978;还不能直接被JS调用!为了能让我们使用,我们需要执行一个或更多步骤:
    $ cargo install wasm-bindgen-cli # only needed once
    $ wasm-bindgen target/wasm32-unknown-unknown/debug/wasm_greet.wasm --out-dir .

    很多不可思议的事情发生都发生在这个步骤中:wasm-bindgen CLI工具?#20801;?#20837;的wasm文件做后期处理,使它变的“suitable”可用。

    我们待会再来看“suitable”的意思,现在我们可以肯定的说,如果我们引入刚创建的wasm_greet.js文件(wasm-bindgen工具创建的),我们已经获取到了在Rust中定义的greet函数。

    最终我们接下来要做的是使用bundler对其打包,然后创建一个HTML页面运行我们的代码。

    在写这篇文章的时候,只有Webpack’s 4.0 release对WebAssembly的使用有足够的支持(尽管暂时已经有了 Chrome caveat)。

    总有一天,更多的bundler也会接着支持WebAssmbly。在这我不再描述细节,但是你可以看一下在Github仓库里的example配置。?#36824;?#22914;果我们看内容,这个页面中我们的JS在看起来是这样的:
    const rust = import("./wasm_greet");
    rust.then(m => m.greet("World!"));

    …就是这些了!现在打开我们的网页?#31361;嵯允?#19968;个不错的“Hello, World!”对话框,这就是Rust驱动的。

    2、wasm-bindgen是如何工作的
    唷,那是一个巨大的“Hello, World!”。让我们深入了解一下更多的细节,以了解后台发生了什么以及该工具是如何工作的。

    wasm-bindgen最重要的方面之一就是它的集成基本上是建立在一个概念之上的,即一个wasm模块仅是另一种ES模块。例如,在上述中我们想要一个带有如下签名的ES模块(在Typescript中):
    export function greet(s: string);

    WebAssembly无法在本地执行此操作(请记住,它目前只支持数字),所以我们依靠wasm-bindgen来填补空?#20303;?

    在上述的最后一步中,当我们运行wasm-bindgen工具?#20445;?#20320;会注意到wasm_greet.js文件与wasm_greet_bg.wasm文件一起出现。前者是我们想要的?#23548;蔎S接口,执行任何必要的处理以调用Rust。* _bg.wasm文件包含?#23548;?#30340;实现和我们所有的编译后的代码。

    我们可以通过引入 ./wasm_greet 模块得到 Rust 代码愿意暴露出来的东西。我们已经看到了是如何集成的,可以继续看看执行的结果如何。首先是我们的示例:
    const rust = import("./wasm_greet");
    rust.then(m => m.greet("World!"));

    我们在这里以异步的方式导入接口,等待导入完成(下载和编译 wasm)。然后调用模块的 greet 函数。

    注: 这里用到的异步加载目前需要 Webpack 来实现,但总会不需要的。而且,其它打包工具可能没有此功能。

    如果我们看看由 wasm-bindgen 工具为 wasm_greet.js 文件生成的内容,会看到像这样的代码:
    import * as wasm from './wasm_greet_bg';
    
    // ...
    
    export function greet(arg0) {
        const [ptr0, len0] = passStringToWasm(arg0);
        try {
            const ret = wasm.greet(ptr0, len0);
            return ret;
        } finally {
            wasm.__wbindgen_free(ptr0, len0);
        }
    }
    
    export function __wbg_f_alert_alert_n(ptr0, len0) {
        // ...
    }

    注: 记住这是生成的,未经优化的代码,它可能既不优雅也不简洁!!在 Rust 中通过 LTO(Link Time Optimization,连接时优化)创建新的发行版,再通过 JS 打包工具流程(压缩)之后,可能会精简一些。

    现在可以了解如何使用wasm-bindgen来生成greet函数。在底层它仍然调用wasm的greet函数,但是它是用一个?#21018;?#21644;长度来调用的而不是用字符串。

    了解passStringToWasm的更多细节可以访问Lin Clark’s previous post。它包含了所有的模板,对我们来说这是除了wasm-bindgen工具以外还需要去写的东西!然后我们接下来看__wbg_f_alert_alert_n函数。

    进入更深一层,下一个我们?#34892;?#36259;的就是WebAssmbly中的greet函数。为了了解这个,我们先来看Rust编译器能访问到的代码。注意像上面生成的这种JS wrapper,在这里你不用写greet的导出符号,#[wasm_bindgen]属性会生成一个shim,由它来为你翻译,命名如下:
    pub fn greet(name: &str) {
        alert(&format!("Hello, {}!", name));
    }
    
    #[export_name = "greet"]
    pub extern fn __wasm_bindgen_generated_greet(arg0_ptr: *mut u8, arg0_len: usize) {
        let arg0 = unsafe { ::std::slice::from_raw_parts(arg0_ptr as *const u8, arg0_len) }
        let arg0 = unsafe { ::std::str::from_utf8_unchecked(arg0) };
        greet(arg0);
    }

    现在可以看到原始代码,greet,也就是由#[wasm_bindgen]属性插入的看起来有意思的函数__wasm_bindgen_generated_greet。这是一个导出函数(用#[export_name]和extern关键词来指定的),?#38382;?#20026;JS传进来的?#21018;?长度对。在函数中它会将这个?#21018;?长度转换为一个&str (Rust中的一个字符串),然后将它传递给我们定义的greet函数。

    从另一个方面看,#[wasm_bindgen]属性生成了两个wrappers:一个是在JavaScript中将JS类型的转换为wasm,另外一个是在Rust中接收wasm类型并将其转为Rust类型。

    现在我们来看wrappers的最后一块,即alert函数。Rust中的greet函数使用标准format!宏来创建一个新的字符串然后传给alert。回想当我们声明alert方法的时候,我们是使用 #[wasm_bindgen]声明的,现在我们看看在这个函数中暴露给rustc的内容:
    fn alert(s: &str) {
        #[wasm_import_module = "__wbindgen_placeholder__"]
        extern {
            fn __wbg_f_alert_alert_n(s_ptr: *const u8, s_len: usize);
        }
        unsafe {
            let s_ptr = s.as_ptr();
            let s_len = s.len();
            __wbg_f_alert_alert_n(s_ptr, s_len);
        }
    }

    这并不是我们写的,但是我们可以看看它是怎么变成这样的。alert函数事实上是一个简化的wrapper,它带有Rust的 &str然后将它转换为wasm类型(数字)。它调用了我们在上面看到过的比较有意思的函数__wbg_f_alert_alert_n,然而它奇怪的一点就是#[wasm_import_module]属性。

    在WebAssembly中所有导入的函数都有一个其存在的模块,而且由于wasm-bindgen构建在ES模块之上,所以这也将被转译为ES模块导入!

    目前__wbindgen_placeholder__模块?#23548;?#19978;并不存在,但它表示该导入将被wasm-bindgen工具重写,以从我们生成的JS文件中导入。

    最后,对于最后一部分的疑惑,我们得到了我们所生成的JS文件,其中包含:
    export function __wbg_f_alert_alert_n(ptr0, len0) {
        let arg0 = getStringFromWasm(ptr0, len0);
        alert(arg0)
    }

    哇! 事实证明,这里隐藏着相当多的东西,我们从JS中的浏览器中的警告都有一个相对较长的知识链。?#36824;?#19981;要害怕,wasm-bindgen的核心是所有这些基础设施都被隐藏了! 你只需要在随便使用几个#[wasm_bindgen]编写Rust代码即可。然后你的JS可以像使用另一个JS包或模块一样使用Rust了。

    wasm-bindgen还能做什么

    wasm-bindgen项目在这个领域内志向远大,我们在此不再详细赘述。探索wasm-bindgen中的功能一个有效的方法就是探索示例目录,这些示例涵盖了从我们之前看到的Hello World! 到在Rust中对DOM节点的完全操作。

    wasm-bindgen高级特性如下:

    • 引入JS结构,函数,对象等来在wasm中调用。你可以在一个结构中调用JS方法,也可以访问属性,这给人一种Rust是“原生”的感觉,让人觉得你曾经写过的Rust #[wasm_bindgen] annotations都可以连接了起来。
    • 将Rust结构和函数导出到JS。与只用JS使用数字类型来工作相比,你可以导出一个Rust结构并在JS中转换成一个类。然后可以将结构传递,而不是只使用整?#38382;道创?#36882;。 smorgasboard 这个例子可以让你体会支持的互操作特性。
    • 其他各种各样的特性例如从全局?#27573;?#20869;导入(就像alert函数),在Rust中使用一个Result来获取JS异常,以及在Rust程序中通用方法模拟存储JS值。
    如果你想了解更多的功能,继续阅读 issue tracker

    3、wasm-bindgen接下来做什么?

    在我们结束之前,我想花一点时间来下描述wasm-bindgen的未来愿景,因为我认为这是当今项目最激动人心的一方面。

    不仅仅支持Rust

    从第1天起,wasm-bindgen CLI工具就设计成了多语言支持的。尽管Rust目前是唯一被支持的语言,但该工具也可以嵌入C或C++。 #[wasm_bindgen]属性创建了可被wasm-bindgen工具解析并随后删除的输出(* .wasm)文件的自定义部分。

    本节介绍要生成哪些JS绑定以及它们的接口是什么。这个描述中没有关于Rust的特定部分,因此C ++编译器插件可以很容易地创建该部分,并通过wasm-bindgen工具进行处理。

    我觉得这个方面特别令人振奋,因为我相信它使像wasm-bindgen这样的工具成为WebAssembly和JS集成的标准做法。希望所有编译为WebAssembly的语言都能受益,并且可以被bundler自动识别,以避免上述几乎所有的配置和构建工具。

    自动绑定JS生态

    使用#[wasm_bindgen] 宏导入功能唯一不好的一面就是你必须将所有东西都写出来,还要保证没有任何错误。这种让人觉得很单调(而且易错)的操作的自动化?#38469;?#24050;经成熟了。

    所有的web APIs?#21152;蒞ebIDL指定,而?#20197;趃enerate #[wasm_bindgen] annotations from WebIDL是可行的。这个就意味着你不需要像前面一样定义alert函数,而是你只需要写下面这些:
    #[wasm_bindgen]
    pub fn greet(s: &str) {
        webapi::alert(&format!("Hello, {}!", s));
    }

    在这个例子中,WebIDL对web APIs的描述可以完全自动生成webapi集?#24076;?#20445;证没有错误。

    我们甚至可以将自动化更进一步,TypeScript组织已经做了这方面的复杂工作,参照generate #[wasm_bindgen] from TypeScript as well。可以免费用npm上的TypeScript自动绑定任何包!

    比 JS DOM 操作更快的性能

    最后要说的事情对 wasm-bindgen 来说也很重要:超快的 DOM 操作 —— 这是很多 JS 框架的终极目标。如今需要使用一些中间工具来调用 DOM 函数,这些工具正在由 JavaScript 实现转向 C++ 引擎实现。然而,在 WebAssembly 来临之后,这些工具并非必须。WebAssembly 是有类型的。

    从第一天起,wasm-bindgen 代码生成的设计就考虑到了将来的宿主绑定方?#28014;?#24403;这一特征出现在 WebAssembly 之后,我们可以直接调用导入的函数,而不需要 wasm-bindgen 的中间工具。

    此外,它使得 JS 引擎积极优化 WebAssembly 对 DOM 的操作,使其对类型的支持更好,而?#20197;?#35843;用 JS 的时候不再需要进行?#38382;?#39564;证。在这一点上,wasm-bindgen 不仅在操作像 string 这样的富类型变得容易,还提供了一流的 DOM 操作性能。

    收工

    我自己发现使用WebAssembly是异常令人振奋的,不仅仅是因为其社区,还因为其如此快速地在进度上突飞猛进。wasm-bindgen工具拥有光明的未来。它使JS和诸如Rust这样的编程语言之间的互操作性变成了一流的体验,并且随着WebAssembly的不断发展它也将提供了长期的好处。

    试着给wasm-bindgen一次机会,因功能需求而创建一个问题,亦或继续保持参与Rust和WebAssembly!

    关于Alex Crichton(作者)
    Alex是Rust核心团队的成员之一,自2012年底以来一直从事于Rust。目前他正在帮助WebAssembly Rust Working Group使得Rust + Wasm成为最佳体验。Alex还帮助维护Cargo(Rust的包管理器),Rust标准库以及Rust的发布和CI的基础架构。
来自: oschina
0
0
评论 共 1 条 请登录后发表评论
1 楼 xxbb77 2018-11-12 16:32
非常?#34892;?谢谢分享

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • 深入认识javascript中得eval函数

    深入认识javascript中得eval函数深入认识javascript中得eval函数深入认识javascript中得eval函数

  • 深入浅出Rust

    深入浅出Rust,一本介绍Rust语言的好书,Mozilla 公司的最新语言

  • 全方位深入理解JavaScript面向对象

    JavaScript面向对象程序设计 本文会碰到的知识点: 原型、原型链、函数对象、普通对象、继承 读完本文,可以学到 面向对象的基本概念 JavaScript对象属性 理解JavaScript中的函数对象与普通对象 理解prototype和proto 理解原型和原型链 详解原型链相关的Object方法 了解如何用ES5模拟类,以及各种方式的优缺点 了解如何用ES6实现面向对象 目录...

  • 深入认识CPU

    学习汇编的时候,对cpu的认识有助于对汇编语言的理解,因为汇编语言功能可以直接对cpu中的寄存器进行操作。 以8086cpu为例子,8086cpu是16位的,那么16位cpu具有哪些性质呢? 运算器一次最多可以处理16位的数据 寄存器的最大宽度为16位 寄存器和运算器之间的通路为16位 1.cpu组成 控制部件单元(Control unit):主要是负责对指令,并且发出为完成每条指令所要执行的各个

  • Rust 程序设计语言(第二版).pdf

    Rust 程序设计语言(第二版).pdf 学习rust必备,rust是mozilla开发,rust的作者也是javascript的作者。

  • 打败 Python、JS、C# 成最受?#38431;?#32534;程语言,是时候掌握 Rust 了吗?

  • 对html,css,和JavaScript的粗浅认识

    1.css是层叠样式表,负责给文档描述样式; ?2.css就是给html穿?#36335;?#30340;,我们把网页视为人,html就是身体,css就是?#36335;?em class='related_suggestion_highlight'>JavaScript就是言行举止;

  • Rust: 亿元估值AI网红代码的不同版本

    Rust版本 use std::io; /* AI 核心代码 --rust 版本 估值 1个亿 */ fn main() { loop { let mut str_in = String::new(); io::stdin() .read_line(&mut str_in) .ok() ...

  • IOT语义互操作性之语义

    这个系列文章描述了一个单一的语义数据模型来支持物联网和建筑、企业和消费者的数据转换。 这种模型必须简单可扩展, 以便能够在各行业领域之间实现插件化和互操作性。 对于一个目前从事智能硬件的?#19979;?#20892;,觉得这...

  • rust 网络爬虫相关收集

    Hyper :一个快速和正确的 Rust HTTP实现。 https://github.com/hyperium/hyper html5ever:Rust html解析库 https://github.com/servo/html5ever 附:https://github.com/carllerche/curl-rust ...

  • 随便谈谈Rust错误处理

    错误处理? 其实我一直不能太分清楚什么是错误什么是异常,?#36824;?#25105;倒是觉?#20204;?#20998;这些个东西意义不大,重要的是认清?#23616;省?#19968;个程序在运行过程中总会碰到一些错误,有的是因为用户的不当操作,有的是因为期望的结果没有发生,当然还有直接就是无法?#25351;?#30340;程序bug,无论如何,这些问题,如果我们考虑到了,都是需要一套解决方案的,所以我们就来稍微谈谈这些解决方法,还有我最近学到Rust的解决方法。 从Error Cod...

  • js深入原理的学习线路总结

    https://github.com/cbbfcd/all-of-javascript/blob/master/%E6%B7%B1%E5%85%A5%E5%88%B0%E4%B8%8D%E8%83%BD%E5%86%8D%E6%B7%B1%E5%85%A5%E4%B9%8BJS%E5%A4%A7%E6%B3%95%E7%B3%BB%E5%88%97/memory.md

  • rust 的内存管理

  • 计算机各部件认识

  • 深入理解Tomcat

    简介tomcat是一个web服务器,运行jsp和servlet,使用HTTP与?#31361;?#31471;(通常是浏览器)进?#22411;?#20449;。构成下图是tomcat的架构,可以看出:核心内容是Connector和Container组件。一个Server服务器中可能有多个Service,Service可以暂时理解为“服务”。Server负责管理Service的生命周期,让外界能够访问。Service将Connector和Contai

  • 对JDK的深入理解

  • tigase深入理解xmpp服务器-概念总览

    Tigase是基于组件,插件,连接器实现的框架,Tigase提供了及其灵活的接口,供我们实现自定义组件,插件,连接器。1、组件:是侧重实现功能的,例如发?#32423;?#38405;,Socks5代理,开发者可以为组件配置独立的数据库,统计信息等,具体可见开发文档。组件是Tigase服务器的主要元素。组件是一段更大的代码,可以具有单独的地址,接收和发送节,并配置为响应众多事件。为Tigase服务器实现的示例组件包括:c2...

  • TCP/UDP深入理解

  • IOT语义互操作性之本体论

    这个系列文章描述了一个单一的语义数据模型来支持物联网和建筑、企业和消费者的数据转换。 这种模型必须简单可扩展, 以便能够在各行业领域之间实现插件化和互操作性。 对于一个目前从事智能硬件的?#19979;?#20892;,觉得这...

Global site tag (gtag.js) - Google Analytics 玩北京pk10久玩必输