• 走进墨玉—天山网专题报道 2019-04-19
  • 木垒积极打造农家生活体验区 2019-04-03
  • 安徽省高校公共体艺教育推行俱乐部制 2019-03-26
  • 女性之声——全国妇联 2019-03-23
  • 险!小孩头卡防盗栏 民警爬窗外托举 成功解救 2019-03-23
  • 中国侨联直属机关党委举办党务干部培训班 2019-03-21
  • 雷佳音曾担任佟丽娅婚礼司仪 原因竟是不用随红包 2019-03-17
  • 电商扶贫,山“疙瘩”成“金蛋蛋” 2019-03-17
  • 湖州师院志愿者:携手弱势群体共谱青春乐章 2019-03-14
  • 阳泉计划今年在全省率先整体脱贫 2019-03-13
  • 货币政策取向仍以国内为主 2019-03-13
  • 中华人民共和国防震减灾法 2019-02-22
  • 的神话少了没人相信我,继续,希望你造出更多的神话来 2019-02-22
  • 七彩人生、林氏木业等品牌产品抽检不合格 2019-02-06
  • 阳泉消防开展冬训“集团化”练兵 2019-02-06
  • 阅读更多
    引用
    原文: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节点相关的文档交互,而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}!`);
    }

    不过在这个例子里要注意的是,我们将把它用Rust编写。这里已经发生了很多我们必须要处理的事情:
    • JavaScript将会调用一个WebAssembly ??? ??槊?greetexport.
    • Rust函数将一个字符串作为输入参数,也就是我们要打招呼的名字。
    • 在内部Rust会生成一个新的字符串,也就是传入的名字。
    • 最后Rust会调用JavaScript的 alert函数,以刚创建的字符串作为参数。
    启动第一步,我们创建一个新的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的依赖。这里的依赖包含了我们使用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文件实际上还不能直接被JS调用!为了能让我们使用,我们需要执行一个或更多步骤:
    $ cargo install wasm-bindgen-cli # only needed once
    $ wasm-bindgen target/wasm32-unknown-unknown/debug/wasm_greet.wasm --out-dir .

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

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

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

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

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

    …就是这些了!现在打开我们的网页就会显示一个不错的“Hello, World!”对话框,这就是Rust驱动的。

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

    wasm-bindgen最重要的方面之一就是它的集成基本上是建立在一个概念之上的,即一个wasm??榻鍪橇硪恢諩S???。例如,在上述中我们想要一个带有如下签名的ES??椋ㄔ赥ypescript中):
    export function greet(s: string);

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

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

    我们可以通过引入 ./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函数,但是它是用一个指针和长度来调用的而不是用字符串。

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

    进入更深一层,下一个我们感兴趣的就是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关键词来指定的),参数为JS传进来的指针/长度对。在函数中它会将这个指针/长度转换为一个&str (Rust中的一个字符串),然后将它传递给我们定义的greet函数。

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

    现在我们来看wrappers的最后一块,即alert函数。Rust中的greet函数使用标准format!宏来创建一个新的字符串然后传给alert?;叵氲蔽颐巧鱝lert方法的时候,我们是使用 #[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__??槭导噬喜⒉淮嬖?,但它表示该导入将被wasm-bindgen工具重写,以从我们生成的JS文件中导入。

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

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

    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中转换成一个类。然后可以将结构传递,而不是只使用整形数值来传递。 smorgasboard 这个例子可以让你体会支持的互操作特性。
    • 其他各种各样的特性例如从全局范围内导入(就像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] 宏导入功能唯一不好的一面就是你必须将所有东西都写出来,还要保证没有任何错误。这种让人觉得很单调(而且易错)的操作的自动化技术已经成熟了。

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

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

    我们甚至可以将自动化更进一步,TypeScript组织已经做了这方面的复杂工作,参照generate #[wasm_bindgen] from TypeScript as well??梢悦夥延胣pm上的TypeScript自动绑定任何包!

    比 JS DOM 操作更快的性能

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

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

    此外,它使得 JS 引擎积极优化 WebAssembly 对 DOM 的操作,使其对类型的支持更好,而且在调用 JS 的时候不再需要进行参数验证。在这一点上,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
    非常感谢 谢谢分享

    发表评论

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

    相关推荐

    • 深入认识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的作者。

    • 深入理解数组的运算

      数组运算讲解:基本每个同学接触到数组时都觉得它很简单,其实你还没有深入了解它,那么下面我将用例题讲解的方式带你深入数组,在讲解之前你必须了解以下知识点: 若有数组arr[]={1,2,3,4};sizeof(arr)里面的数组名代表整个数组,换句话说就是当数组名单独出现在sizeof()中时,数组名只表示整个数组,&arr,这里的数组名也代表整个数组,除这两种方式的其余形式数组名均代表首元素地址。

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

    • 计算机各部件认识

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

      1.css是层叠样式表,负责给文档描述样式; ?2.css就是给html穿衣服的,我们把网页视为人,html就是身体,css就是衣服,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语义互操作性之语义

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

    • tigase组件深入理解-xmpp协议总览

      tigase组件的实现,例如pubsub,muc等组件都是基于xmpp协议来实现的,所以在这之前需要对xmpp协议的整体有所了解,这样在tigase二次开发,例如基于socks5实现语音,视频,图片等功能的时候能够深入理解。XMPP(可扩展消息处理现场协议)是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线现场探测。它在促进服务器之间的准即时操作。这个协议可能最终允许因特网用户向...

    • rust 网络爬虫相关收集

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

    • 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错误处理

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

    • rust 的内存管理

    • 深入理解Tomcat

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

    • git的浅谈深入理解

    • java对象深入理解。

    Global site tag (gtag.js) - Google Analytics
  • 走进墨玉—天山网专题报道 2019-04-19
  • 木垒积极打造农家生活体验区 2019-04-03
  • 安徽省高校公共体艺教育推行俱乐部制 2019-03-26
  • 女性之声——全国妇联 2019-03-23
  • 险!小孩头卡防盗栏 民警爬窗外托举 成功解救 2019-03-23
  • 中国侨联直属机关党委举办党务干部培训班 2019-03-21
  • 雷佳音曾担任佟丽娅婚礼司仪 原因竟是不用随红包 2019-03-17
  • 电商扶贫,山“疙瘩”成“金蛋蛋” 2019-03-17
  • 湖州师院志愿者:携手弱势群体共谱青春乐章 2019-03-14
  • 阳泉计划今年在全省率先整体脱贫 2019-03-13
  • 货币政策取向仍以国内为主 2019-03-13
  • 中华人民共和国防震减灾法 2019-02-22
  • 的神话少了没人相信我,继续,希望你造出更多的神话来 2019-02-22
  • 七彩人生、林氏木业等品牌产品抽检不合格 2019-02-06
  • 阳泉消防开展冬训“集团化”练兵 2019-02-06
  • 半全场只对一个 时时彩自由的百科天堂 双色球71复式玩法 快乐8开奖直播 体彩20选5官网中奖福地 福建福彩 时时彩趋势分析软件 北京快中彩中奖规则 足彩胜负彩过关统计 秒速时时彩个位计划 pk10赛车历史开奖记录 京东彩票扫码 北单3串1进球平均奖金 时时彩走势图怎么看 体彩排列五综合走势图 云南十一选五走势图