用Rust创建内存和类型安全的Node.js模块
Node是开发应用程序的最著名的平台之一,它在V8引擎上运行,并在网络浏览器外执行JavaScript代码。尽管Nodejs很受欢迎,而且具有异步或非阻塞行为等很酷的特性,但与Go和Rust等语言或框架相比,Nodejs的速度并不高。
另一方面,Rust是一种强类型和编译的语言,它具有出色的功能,如闭合和匿名函数、丰富的标准库(std)、强类型、多态性、出色的构建系统,以及包管理器Cargo。Rust也是一种内存安全语言,因为它在编译时进行内存检查。有了Rust的这些优点,你是否想过,你是否可以用Rust的类型和内存安全来编写JavaScript?这就是Neon发挥作用的地方。Neon允许你用Rust编写JavaScript。
什么是Neon
Neon是一个库和工具链,用于将Rust嵌入到你的Node.js应用程序和库中。Neon允许你编写类型安全、内存安全、无崩溃的本地Node模块,保证Rust的并行性与线程安全。
使用Neon的好处
除了内存和类型安全之外,还有更多的理由让你在Node中使用Rust嵌入。
- 平行编程和线程
- 性能
- 访问本地操作系统专用库
- 通过Cargo进入Rust的生态体系
Neon还提供了一个命令行界面和工作流程,并与惯例绑定,消除了构建本地Node模块的麻烦。
探索Neon的API
让我们来探索Neon的API,看看我们如何用Rust编写一个原生节点模块。首先,让我们看看let是如何处理类型的。
- JsValue 位于类型层次结构的顶端,可以指代任何JavaScript值。
- JsObject位于对象类型层次结构的顶端。对象类型都实现了Object trait,它允许获取和设置属性。像JsFunction、JsArray、JsDate和JsError 等类型都属于这个部分**。**
- JsNumber、JsBoolean、JsString、JsNull和JsUndefined是不属于对象类型的内置JavaScript数据类型(原始类型)。
接下来,让我们继续讨论入口点 **src/lib.rs**文件。该文件由主函数组成,它有一个特殊的霓虹灯注解,叫做 [**#[neon::main]**](https://docs.rs/neon/0.9.1/neon/attr.main.html)它在模块加载时被执行(Neon模块的初始化)。如果你愿意,你可以重新命名这个函数,只要你保留这个注释。它将成为你的模块的入口点。
#[neon::main]fn main(mut cx: ModuleContext) -> NeonResult<()> { cx.export_function("hello", hello)?; Ok(())}
由main方法导出的hello函数,将接受一个名为 [**FunctionContext**](https://docs.rs/neon/0.9.1/neon/context/type.FunctionContext.html)(的参数(它为Neon函数提供了对JavaScript运行时的访问权),并返回一个JavaScript字符串。你可以注意到,所有的霓虹灯函数都会返回一个叫做[**JsResult**](https://docs.rs/neon/0.9.1/neon/result/type.JsResult.html)这是因为所有Neon函数都有可能抛出一个JavaScript异常。
建立一个Nodejs模块
让我们通过建立一个节点模块来深入了解Neon。在这个例子中,让我们创建一个模块,提供函数来生成形状的区域(方形、圆形、三角形等)。但在开始之前,我们有一些先决条件,你需要最新版本或LTS版本的Nodejs和Rust(如果你使用windows,则需要visual studio构建工具)。安装完所有先决条件后,让我们继续初始化项目。
npm init neon neon-area
上述命令将用Cargo.toml和package.json文件初始化项目,这两个文件保存了Rust和Nodejs的包的详细信息(依赖关系、模块详细信息)。你会注意到,在项目根目录中会有一个新的目录,名为 **src/**, ,其中包括一个名为lib.rs的文件,所有的魔法都发生在Rust上。
use neon::prelude::*;
在上面的代码中,你将导出一个返回字符串的函数(在javaScript中),作为一个名为 "hello "的模块属性,以后你可以通过导入模块的方式通过JavaScript访问它。要先创建一个Node模块,你必须使用以下命令构建上述代码。
npm run build -- --release
这将编译你的Rust代码,并在你的项目根部制作一个名为index.node的本地Node模块。这些.node文件是Node Addons(二进制模块),你应该可以用JavaScript对其使用 "require() "来访问该模块。
const {hello} = require("./index.node");
现在让我们继续使用Neon构建我们的区域模块。首先,让我们创建一个名为Area的结构,然后继续实现这些方法。
struct Area { pub pi: f64,}
现在,让我们通过main方法将这些函数导出为一个模块。让我们看一下这个文件的最终代码 **src/lib.rc**文件的最终代码。
现在,是时候再次构建模块并在JavaScript应用程序中使用生成的模块了。
执行上面的代码,终端会出现结果。
总结
由于Rust所带来的类型和内存安全,Neon是编写本地节点模块的最佳方式之一。Neon还暴露了用于调度异步事件的JavaScript事件循环,因此你可以在后台线程中运行昂贵的或长期的计算,而不会阻塞JavaScript线程。
干杯!