Rust与Deno的桥梁:deno_napi插件库的使用,实现高性能Node-API与Deno的交互
Rust与Deno的桥梁:deno_napi插件库的使用,实现高性能Node-API与Deno的交互
概述
deno_napi是一个用于连接Rust与Deno的插件库,它通过Node-API标准实现了两者之间的高性能交互。该实现保持了与Node.js相似的文件组织方式,确保良好的兼容性。
添加新函数
1. 声明符号
在symbol_exports.json中添加需要导出的函数符号:
{
"symbols": [
"napi_get_undefined",
"napi_get_null",
"napi_get_boolean"
]
}
2. 实现函数
在适当的Rust文件中(如js_native_api.rs)编写实现:
#[napi_sym::napi_sym]
fn napi_get_boolean(
env: *mut Env, // 环境指针
value: bool, // 输入布尔值
result: *mut napi_value, // 输出结果指针
) -> Result {
// 实现逻辑...
Ok(())
}
3. 更新符号列表
运行生成脚本更新符号列表:
deno run --allow-write tools/napi/generate_symbols_lists.js
测试示例
JavaScript测试代码
// 测试napi_get_boolean功能
import { assertEquals, loadTestLibrary } from "./common.js";
const lib = loadTestLibrary();
Deno.test("napi get boolean", function () {
assertEquals(lib.test_get_boolean(true), true);
assertEquals(lib.test_get_boolean(false), false);
});
Rust测试实现
use napi_sys::Status::napi_ok;
use napi_sys::ValueType::napi_boolean;
use napi_sys::*;
extern "C" fn test_boolean(
env: napi_env,
info: napi_callback_info,
) -> napi_value {
// 获取回调信息
let (args, argc, _) = crate::get_callback_info!(env, info, 1);
assert_eq!(argc, 1);
// 验证参数类型
let mut ty = -1;
assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok);
assert_eq!(ty, napi_boolean);
// 返回原始值
value
}
模块注册
在lib.rs中注册测试模块:
mod boolean;
#[no_mangle]
unsafe extern "C" fn napi_register_module_v1(
env: napi_env,
exports: napi_value,
) -> napi_value {
boolean::init(env, exports);
exports
}
运行测试:
cargo test -p tests/napi
完整开发示例
1. 配置Cargo.toml
[dependencies]
deno_napi = "0.143.0" # Deno的Node-API支持
napi-sys = "0.5" # Node-API系统绑定
2. Rust模块实现
use napi_sys::*;
use std::os::raw::c_void;
// 模块注册函数
#[no_mangle]
pub unsafe extern "C" fn napi_register_module_v1(
env: napi_env,
exports: napi_value,
) -> napi_value {
// 定义要导出的hello函数
let desc = napi_property_descriptor {
utf8name: "hello\0".as_ptr() as *const _,
name: std::ptr::null_mut(),
method: Some(hello),
getter: None,
setter: None,
value: std::ptr::null_mut(),
attributes: napi_default,
data: std::ptr::null_mut(),
};
// 将函数添加到exports对象
napi_define_properties(env, exports, 1, &desc);
exports
}
// hello函数实现
unsafe extern "C" fn hello(
env: napi_env,
info: napi_callback_info,
) -> napi_value {
let mut result: napi_value = std::ptr::null_mut();
// 创建返回字符串
napi_create_string_utf8(
env,
"Hello from Rust!\0".as_ptr() as *const _,
15,
&mut result,
);
result
}
3. Deno调用代码
// 加载编译好的Rust库
const lib = Deno.dlopen("./target/debug/libexample.dylib", {
napi_register_module_v1: {
parameters: ["pointer", "pointer"],
result: "pointer"
},
});
// 初始化模块
const exports = new Deno.UnsafePointerObject(
lib.symbols.napi_register_module_v1(
new Deno.UnsafePointer(0n),
new Deno.UnsafePointer(0n)
)
);
// 调用Rust函数
const result = Deno.core.unescapePointer(expts.hello());
console.log(result); // 输出: "Hello from Rust!"
4. 构建和运行
# 编译Rust库
cargo build
# 运行Deno测试(需要启用不稳定特性)
deno run --allow-ffi --unstable test.ts
平台注意事项
根据操作系统不同,生成的库文件扩展名也不同:
- macOS: .dylib
- Linux: .so
- Windows: .dll
在Deno代码中需要根据实际平台调整库文件路径。
1 回复
Rust与Deno的桥梁:deno_napi插件库的使用
介绍
deno_napi是一个允许Rust代码通过Node-API(N-API)与Deno交互的插件库。它提供了一种高性能的方式来扩展Deno的功能,利用Rust的性能优势和Deno的JavaScript运行时能力。
主要特点
- 在Deno环境中使用Rust编写的原生模块
- 通过Node-API标准接口实现互操作
- 高性能的跨语言调用
- 支持异步操作
- 类型安全的接口绑定
完整示例demo
Rust部分 (src/lib.rs)
use napi_derive::napi;
// 简单的加法函数
#[napi]
pub fn sum(a: i32, b: i32) -> i32 {
a + b
}
// 异步乘法函数
#[napi]
pub async fn async_multiply(a: i32, b: i32) -> i32 {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
a * b
}
// 带错误处理的除法函数
#[napi]
pub fn divide(a: i32, b: i32) -> napi::Result<i32> {
if b == 0 {
return Err(napi::Error::new(
napi::Status::GenericFailure,
"Cannot divide by zero".to_string(),
));
}
Ok(a / b)
}
// 数组映射函数,接受JavaScript回调
#[napi]
pub fn map_array(arr: Vec<i32>, callback: napi::JsFunction) -> napi::Result<Vec<i32>> {
let mut result = Vec::new();
for item in arr {
let new_item: i32 = callback.call(None, &[item.into()])?.try_into()?;
result.push(new_item);
}
Ok(result)
}
// Person结构体定义
#[napi]
pub struct Person {
pub name: String,
pub age: i32,
}
#[napi]
impl Person {
#[napi(constructor)]
pub fn new(name: String, age: i32) -> Self {
Self { name, age }
}
#[napi]
pub fn introduce(&self) -> String {
format!("Hi, I'm {}, {} years old", self.name, self.age)
}
#[napi]
pub fn birthday(&mut self) {
self.age += 1;
}
}
Deno部分 (example.js)
import { load } from "https://deno.land/x/deno_napi/mod.ts";
// 加载编译好的Rust模块
const {
sum,
asyncMultiply,
divide,
mapArray,
Person
} = await load("./target/release/libdeno_napi_example.so");
// 测试同步函数
console.log("1 + 2 =", sum(1, 2)); // 输出: 1 + 2 = 3
// 测试异步函数
(async () => {
console.log("3 * 4 =", await asyncMultiply(3, 4)); // 1秒后输出: 3 * 4 = 12
})();
// 测试错误处理
try {
console.log("10 / 0 =", divide(10, 0));
} catch (e) {
console.error("Error:", e.message); // 输出: Error: Cannot divide by zero
}
// 测试回调函数
const squared = mapArray([1, 2, 3, 4], (x) => x * x);
console.log("Squared array:", squared); // 输出: Squared array: [1, 4, 9, 16]
// 测试类和方法
const alice = new Person("Alice", 30);
console.log(alice.introduce()); // 输出: Hi, I'm Alice, 30 years old
alice.birthday();
console.log("After birthday:", alice.introduce()); // 输出: After birthday: Hi, I'm Alice, 31 years old
构建和运行步骤
- 创建Rust项目并添加依赖
cargo new --lib deno_napi_example
cd deno_napi_example
- 编辑Cargo.toml
[lib]
crate-type = ["cdylib"]
[dependencies]
napi = { version = "2", default-features = false }
napi-derive = "2"
deno_napi = "0.1"
tokio = { version = "1", features = ["rt"] }
- 构建发布版本
cargo build --release
- 运行Deno测试脚本
deno run --allow-read example.js
性能提示
- 尽量减少跨语言调用的次数,批量处理数据
- 对于复杂数据结构,考虑使用ArrayBuffer或SharedArrayBuffer传输
- 长时间运行的任务应该在Rust端使用异步API
注意事项
- 确保Deno版本与deno_napi兼容
- 跨平台时注意库文件扩展名(.so, .dll, .dylib)
- 发布时需要包含编译好的二进制文件