Rust是一种编程语言,它于2010年起源于MozillaResearch。现在所有的大公司都在使用它。亚马逊和微软都认可它是他们系统中C/C++的最佳替代品,但Rust并不止于此。像Figma和Discord这样的公司现在也通过在他们的客户端应用程序中使用Rust来引领潮流。本Rust教程旨在简要介绍Rust、如何在浏览器中使用它以及何时应该考虑使用它。我将从比较Rust和JavaScript开始,然后引导您完成Rust在浏览器中运行的步骤。最后,我将对使用Rust和JavaScript的COVID模拟器Web应用程序进行快速性能评估。简而言之,Rust在概念上与JavaScript非常不同。但有一些相似之处需要指出,所以让我们看看问题的两面。相似之处两种语言都有一个现代的包管理系统。JavaScript有npm,Rust有Cargo。Rust使用Cargo.toml而不是package.json来进行依赖管理。要创建一个新项目,请使用cargoinit,要运行它,请使用cargorun。不会太陌生吧?Rust中有很多你已经从JavaScript中了解到的很酷的特性,只是语法略有不同。使用以下常见JavaScript模式对数组中的每个元素应用闭包:letstaff=[{name:"George",money:0},{name:"Lea",money:500000},];letsalary=1000;staff。forEach((员工)=>{员工.money+=薪水;});在Rust中,我们可以这样写:letsalary=1000;staff.iter_mut().for_each(|employee|{employee.money+=salary;});不可否认,习惯这种语法需要时间,使用竖线(|)而不是括号。但是在克服了最初的尴尬之后,我发现它比另一组括号读起来更清楚。作为另一个例子,这是JavaScript中的对象解构:letpoint={x:5,y:10};let{x,y}=point;同样在Rust中:letpoint=Point{x:5,y:10};letPoint{x,y}=point;主要区别在于,在Rust中我们必须指定类型(Point),更一般地说,Rust需要在编译时知道所有类型。但与大多数其他编译语言不同,编译器会尽可能多地自行推断类型。为了进一步解释这个问题,下面是适用于C++和许多其他语言的代码。每个变量都需要一个明确的类型声明。inta=5;floatb=0.5;floatc=1.5*a;在JavaScript和Rust中,这段代码是有效的:leta=5;letb=0.5;letc=1.5*a;共享功能太多无法一一列举:Rust具有async+await语法。可以像letarray=[1,2,3]一样简单地创建数组。代码组织在具有显式导入和导出的模块中。字符串以Unicode编码,处理特殊字符没有问题。我可以继续说下去,但我想我的观点现在已经很清楚了。Rust具有一组丰富的特性,现代JavaScript中也使用了这些特性。不同之处在于Rust是一种编译语言,这意味着没有运行时来执行Rust代码。应用程序只能在编译器(rustc)发挥其魔力后才能运行。这种方法的好处通常是更好的性能。幸运的是,Cargo负责为我们调用编译器。使用webpack,我们还可以将Cargo隐藏在npmrunbuild后面。通过本指南,只要您为项目设置Rust,就可以保留Web开发人员的正常工作流程。Rust是一种强类型语言,这意味着在编译时所有类型都必须匹配。例如,您不能调用参数类型错误或参数数量错误的函数。编译器会在您在运行时遇到此错误之前为您捕获它。最明显的比较是TypeScript,如果你喜欢TypeScript,你很可能会喜欢Rust。但别担心:如果你不喜欢TypeScript,Rust可能仍然适合你。Rust是近年来从头开始构建的,考虑到了人类在过去几十年中在编程语言设计中学到的一切。结果是一种令人耳目一新的简洁语言。Rust中的模式匹配是我最喜欢的功能之一,其他语言有switch和case来避免像这样的长链:if(x==1){//...}elseif(x==2){//...}elseif(x==3||x==4){//...}//...Rust使用更优雅的匹配如下:matchx{1=>{/*Dosomethingifx==1*/},2=>{/*Dosomethingifx==2*/},3|4=>{/*Dosomethingifx==3||x==4*/},5...10=>{/*Dosomethingifx>=5&&x<=10*/},_=>{/*Catchalothercases*/}}我认为这非常巧妙,我希望JavaScript开发人员也能欣赏这种语法扩展。不幸的是,我们仍然不得不谈论Rust的阴暗面。坦率地说,使用严格的类型系统有时会让人觉得很麻烦。如果您认为C++或Java具有严格的类型系统,请准备好在Rust中体验一番。就个人而言,我喜欢Rust的这一部分。我依赖于一个严格的类型系统,这样我就可以关闭大脑中每当我发现自己在编写JavaScript时就会猛烈燃烧的部分。但是我知道对于初学者来说总是和编译器作对是很烦人的。我们稍后会在Rust教程中看到其中的一些内容。HelloRust现在,让我们用Rust在浏览器中运行一个helloworld,我们首先要确保安装了所有必要的工具。该工具使用rustup安装Cargo+rustc。Rustup是推荐的Rust安装方式,它会安装最新稳定版的Rust编译器(rustc)和包管理器(Cargo)。它将安装最新稳定版本的Rust编译器(rustc)和包管理器(Cargo)。它还管理测试版和每晚构建,但对于本示例而言这不是必需的。在终端中键入cargo--version以检查安装,您应该会看到类似cargo1.48.0(65cbdd2dc2020-10-14)的内容。还要检查Rustup:rustup--version应该生成rustup1.23.0(00924c9ba2020-11-27)。安装wasm-pack。这是为了将编译器与npm集成。通过输入wasm-pack--version来检查安装,它应该会给你类似wasm-pack0.9.1的东西。我们还需要Node和npm。我们有一整篇文章[1]解释了安装这两个的最佳方法。编写Rust代码现在一切都已安装,让我们创建项目。最终代码也可以在这个GitHub存储库[2]中找到。我们从一个编译成npm包的Rust项目开始,然后是导入该包的JavaScript代码。要创建名为hello-world的Rust项目,请使用cargoinit--libhello-world。这将创建一个新目录并生成Rust库所需的所有文件:├──hello-world├──Cargo.toml├──src├──lib.rsRust代码将放在lib.rs中,之前我们必须调整Cargo.toml。它使用TOML[3]定义依赖项和其他包信息。如果您想在浏览器中看到helloworld,请在Cargo.toml的某处添加以下行(例如,在文件末尾)。[lib]crate-type=["cdylib"]这告诉编译器以C兼容模式创建一个库。显然我们在示例中没有使用C。C兼容只是意味着不是Rust特定的,这是我们在JavaScript中使用库所需要的。我们还需要两个外部库,将它们作为单独的行添加到依赖项部分。[dependencies]wasm-bindgen="0.2.68"web-sys={version="0.3.45",features=["console"]}这些是来自crates.io[4]的依赖项,这是Cargo的默认包存储库使用。wasm-bindgen[5]是创建入口点所必需的,我们稍后可以从JavaScript调用它。(您可以在此处找到完整的文档。)值“0.2.68”指定版本。web-sys[6]包含所有WebAPI的Rust绑定,这将使我们能够访问浏览器控制台。请注意,我们必须明确选择控制台功能,我们最终的二进制文件将仅包含如此选择的WebAPI绑定。接下来是lib.rs中的实际代码。可以删除自动生成的单元测试。只需将文件内容替换为以下代码:usewasm_bindgen::prelude::*;useweb_sys::console;#[wasm_bindgen]pubfnhello_world(){console::log_1("Helloworld");}顶部的use语句就是从Othermodules中读取import项目。这类似于JavaScript中的导入)。pubfnhello_world(){...}声明了一个函数。pub修饰符是“public”的缩写,其作用类似于JavaScript中的export。注意#[wasm_bindgen]Rust特定的编译为[WebAssembly(Wasm)](https://webassembly.org/"wasm_bindgen]“)”)。我们在这里需要它来确保编译器将包装函数公开给JavaScript。在函数体中,“Helloworld”被打印到控制台。Rust中的console::log_1()是调用console.log()的包装器。您是否注意到函数调用中的_1后缀?这是因为JavaScript允许可变数量的参数,而Rust不允许。为了解决这个问题,wasm_bindgen为每个参数数量生成一个函数。是的,这会很快变得丑陋!但它有效。web-sys文档[7]中提供了可以在Rust控制台中调用的完整函数列表。现在我们应该一切就绪,尝试使用以下命令编译它。这将下载所有依赖项并编译项目,第一次可能需要一段时间。cdhello-worldwasm-packbuild哈!Rust编译器对我们不满意。错误[E0308]:类型不匹配-->src\lib.rs:6:20|6|console::log_1("Helloworld");|^^^^^^^^^^^^^^expectedstruct`JsValue`,found`str`|=note:expectedreference`&JsValue`foundreference`&'staticstr注意:如果你看到其他错误(error:linkingwithccfailed:exitcode:1)并且你使用的是Linux,则交叉编译缺失依赖。sudoaptinstallgcc-multilib应该可以解决这个问题。正如我之前提到的,编译器是严格的。当它希望将对JsValue的引用作为函数参数时,它不会接受静态字符串。必须进行显式转换才能满足编译器的要求。console::log_1(&"Helloworld".into());方法[into()](https://doc.rust-lang.org/std/convert/trait.Into.html"into("into()")")将一个值转换为另一个值。Rust编译器足够聪明,可以推迟哪些类型参与转换,因为函数签名只留下一种可能性。在这种情况下,它将被转换为JsValue,这是一种由JavaScript管理的值的包装器类型。然后,我们必须添加&以通过引用而不是值传递,否则编译器会再次报错。尝试再次运行wasm-packbuild,如果一切顺利,最后一行应该如下所示:[INFO]::-)Yourwasmpkgisreadytopublish/home/username/intro-to-rust/hello-world/pkg。如果你可以通过这一步,你现在可以手动编译Rust。下一步,我们会将其与npm和webpack集成,它们会自动为我们完成这项工作。JavaScript集成在这个例子中,我决定将package.json放在hello-world目录中。我们也可以为Rust项目和JavaScript项目使用不同的目录,这是一个品味问题。下面是我的package.json文件。最简单的方法是复制它并运行npminstall,或运行npminit并仅复制开发依赖项:{"name":"hello-world","version":"1.0.0","description":"HelloworldappforRustinthebrowser.","main":"index.js","scripts":{"build":"webpack","serve":"webpackserve"},"author":"JakobMeier
