Rust JavaScript引擎绑定库rquickjs的使用,rquickjs提供高性能QuickJS引擎集成与脚本执行功能
Rust JavaScript引擎绑定库rquickjs的使用
rquickjs 是一个高级的 QuickJS JavaScript 引擎绑定库,目标是成为一个易于使用且安全的包装器,类似于 rlua 库。
QuickJS 的主要特点
- 小巧且易于嵌入:只需几个 C 文件,无外部依赖,简单的 hello world 程序在 x86 上仅需 210 KiB 代码
- 快速解释器,启动时间极短:在桌面 PC 单核上运行 75000 个 ECMAScript 测试套件用例仅需约 100 秒
- 几乎完整的 ES2020 支持,包括模块、异步生成器和完整的 Annex B 支持(传统 Web 兼容性)
- 数学扩展:BigDecimal、BigFloat、运算符重载、bigint 模式、数学模式
- 使用引用计数的垃圾回收(减少内存使用并具有确定性行为)和循环移除
- 命令行解释器,使用 JavaScript 实现上下文着色
该 crate 提供的功能
- 完整的异步 Rust 集成
- ES6 Promise 可以作为 Rust future 处理,反之亦然
- 轻松与几乎任何异步运行时或执行器集成
- Rust 和 JS 之间的灵活数据转换
- 许多广泛使用的 Rust 类型可以转换为 JS 类型,反之亦然
- 支持用户定义的分配器
- 可以使用自定义分配器创建 Runtime
- 也完全支持使用 Rust 的全局分配器
- 支持用户定义的模块解析器和加载器
- 支持使用 embed 宏将 JS 模块打包为字节码
- 支持延迟调用 JS 函数
- 完整的 ES6 类支持
- Rust 数据类型可以表示为 JS 类
- 可以通过对象属性访问数据字段
- 同时支持静态和实例成员
示例代码
use rquickjs::{Context, Runtime};
fn main() {
// 创建运行时和上下文
let rt = Runtime::new().unwrap();
let ctx = Context::full(&rt).unwrap();
// 在上下文中执行JavaScript代码
ctx.with(|ctx| {
let result: i32 = ctx.eval("1 + 2").unwrap();
println!("1 + 2 = {}", result); // 输出: 1 + 2 = 3
// 调用JavaScript函数
ctx.eval::<(), _>("function add(a, b) { return a + b; }").unwrap();
let add_fn = ctx.globals().get::<_, fn(i32, i32) -> i32>("add").unwrap();
let sum = add_fn(3, 4);
println!("3 + 4 = {}", sum); // 输出: 3 + 4 = 7
// 从Rust调用JavaScript
let value = ctx.globals().get::<_, String>("undefined").unwrap();
println!("undefined as string: {}", value); // 输出: undefined as string: undefined
});
}
完整示例
下面是一个更完整的示例,展示如何在 Rust 中嵌入 JavaScript 并实现双向交互:
use rquickjs::{Runtime, Context, Function, Object, Value};
use std::error::Error;
// 定义一个Rust函数,可以从JavaScript调用
fn rust_add(a: i32, b: i32) -> i32 {
a + b
}
fn main() -> Result<(), Box<dyn Error>> {
// 创建运行时和上下文
let rt = Runtime::new()?;
rt.set_max_stack_size(512 * 1024); // 设置最大栈大小
let ctx = Context::full(&rt)?;
ctx.with(|ctx| {
// 将Rust函数暴露给JavaScript
let globals = ctx.globals();
globals.set("rustAdd", Function::new(ctx, rust_add))?;
// 执行JavaScript代码
let result: i32 = ctx.eval(r#"
function jsMultiply(a, b) {
return a * b;
}
// 调用Rust函数
const sum = rustAdd(10, 20);
// 调用JavaScript函数
const product = jsMultiply(5, 6);
// 返回计算结果
sum + product
"#)?;
println!("计算结果: {}", result); // 输出: 计算结果: 80
// 获取JavaScript中定义的函数并在Rust中调用
let js_multiply = globals.get::<_, fn(i32, i32) -> i32>("jsMultiply")?;
let product = js_multiply(7, 8);
println!("7 * 8 = {}", product); // 输出: 7 * 8 = 56
Ok(())
})?;
Ok(())
}
开发状态
该绑定功能完整、基本稳定并已准备好使用。错误处理是未来可能改变的唯一部分。一些实验性功能如 parallel 可能无法按预期工作,使用时需自行承担风险。
许可证
该库采用 MIT 许可证
1 回复
Rust JavaScript引擎绑定库rquickjs使用指南
简介
rquickjs 是一个 Rust 绑定库,用于集成 QuickJS JavaScript 引擎。QuickJS 是一个轻量级、可嵌入的 JavaScript 引擎,支持 ES2020 规范,具有极小的体积和出色的性能。
rquickjs 提供了:
- 高性能的 JavaScript 执行环境
- 完整的 ES2020 支持
- 内存安全保证
- Rust 和 JavaScript 之间的无缝互操作
基本使用方法
添加依赖
首先在 Cargo.toml
中添加依赖:
[dependencies]
rquickjs = "0.4"
基本示例
use rquickjs::{Context, Runtime};
fn main() {
// 创建运行时和上下文
let rt = Runtime::new().unwrap();
rt.set_max_stack_size(1024 * 1024); // 设置最大栈大小
let ctx = Context::full(&rt).unwrap();
// 在上下文中执行 JavaScript 代码
ctx.with(|ctx| {
let result: i32 = ctx.eval("1 + 2 * 3").unwrap();
println!("Result: {}", result); // 输出: Result: 7
// 执行更复杂的脚本
let value: String = ctx.eval(r#"
function greet(name) {
return `Hello, ${name}!`;
}
greet("Rust")
"#).unwrap();
println!("{}", value); // 输出: Hello, Rust!
});
}
高级功能
在 Rust 中调用 JavaScript 函数
use rquickjs::{Context, Runtime, Function};
fn main() {
let rt = Runtime::new().unwrap();
let ctx = Context::full(&rt).unwrap();
ctx.with(|ctx| {
// 定义一个 JavaScript 函数
ctx.eval::<(), _>(r#"
function add(a, b) {
return a + b;
}
"#).unwrap();
// 获取并调用该函数
let add: Function = ctx.globals().get("add").unwrap();
let result: i32 = add.call((2, 3).unwrap();
println!("2 + 3 = {}", result); // 输出: 2 + 3 = 5
});
}
在 JavaScript 中调用 Rust 函数
use rquickjs::{Context, Runtime};
fn main() {
let rt = Runtime::new().unwrap();
let ctx = Context::full(&rt).unwrap();
ctx.with(|ctx| {
// 将 Rust 函数暴露给 JavaScript
ctx.globals().set("rustAdd",
|a: i32, b: i32| a + b
).unwrap();
// 在 JavaScript 中调用 Rust 函数
let result: i32 = ctx.eval("rustAdd(5, 7)").unwrap();
println!("5 + 7 = {}", result); // 输出: 5 + 7 = 12
});
}
处理 JavaScript 对象
use rquickjs::{Context, Runtime, Object};
fn main() {
let rt = Runtime::new().unwrap();
let ctx = Context::full(&rt).unwrap();
ctx.with(|ctx| {
// 创建 JavaScript 对象
let obj = Object::new(ctx.clone()).unwrap();
obj.set("name", "Alice").unwrap();
obj.set("age", 30).unwrap();
// 将对象设置为全局变量
ctx.globals().set("person", obj).unwrap();
// 在 JavaScript 中使用该对象
let greeting: String = ctx.eval(r#"
`Hello, my name is ${person.name} and I'm ${person.age} years old.`
"#).unwrap();
println!("{}", greeting); // 输出: Hello, my name is Alice and I'm 30 years old.
});
}
异步执行
use rquickjs::{Context, Runtime, Promise};
use futures::executor::block_on;
fn main() {
let rt = Runtime::new().unwrap();
let ctx = Context::full(&rt).unwrap();
ctx.with(|ctx| {
// 执行异步 JavaScript 代码
let promise: Promise<String> = ctx.eval(r#"
new Promise((resolve) => {
setTimeout(() => resolve("Async operation completed"), 1000);
})
"#).unwrap();
// 等待 Promise 完成
let result = block_on(promise).unwrap();
println!("{}", result); // 输出: Async operation completed
});
}
错误处理
use rquickjs::{Context, Runtime, Error};
fn main() {
let rt = Runtime::new().unwrap();
let ctx = Context::full(&rt).unwrap();
ctx.with(|ctx| {
match ctx.eval::<(), _>("invalid js code") {
Ok(_) => println!("Success"),
Err(Error::Exception) => {
// 获取 JavaScript 异常信息
let exception: String = ctx.eval("String(new Error().stack)").unwrap();
println!("JS Exception: {}", exception);
}
Err(e) => println!("Rust Error: {}", e),
}
});
}
性能提示
- 重用 Runtime 和 Context 对象,它们的创建成本较高
- 对于频繁调用的函数,考虑使用
Function
缓存而不是每次都查找 - 设置适当的栈大小以避免栈溢出
- 对于高性能场景,考虑将数据序列化为 JSON 而不是使用对象属性访问
rquickjs 是一个强大的工具,可以在 Rust 应用程序中安全高效地执行 JavaScript 代码,特别适合需要脚本扩展性或需要与现有 JavaScript 代码集成的场景。
完整示例DEMO
下面是一个整合了多项功能的完整示例:
use rquickjs::{Context, Runtime, Function, Object, Promise};
use futures::executor::block_on;
fn main() {
// 创建运行时和上下文
let rt = Runtime::new().unwrap();
rt.set_max_stack_size(1024 * 1024);
let ctx = Context::full(&rt).unwrap();
ctx.with(|ctx| {
// 示例1: 基本JavaScript执行
let basic_result: i32 = ctx.eval("2 + 2 * 2").unwrap();
println!("Basic calculation: {}", basic_result); // 输出: 6
// 示例2: 在Rust中调用JS函数
ctx.eval::<(), _>(r#"
function multiply(a, b) {
return a * b;
}
"#).unwrap();
let multiply: Function = ctx.globals().get("multiply").unwrap();
let multiply_result: i32 = multiply.call((3, 4)).unwrap();
println!("Multiply result: {}", multiply_result); // 输出: 12
// 示例3: 在JS中调用Rust函数
ctx.globals().set("rustSubtract",
|a: i32, b: i32| a - b
).unwrap();
let subtract_result: i32 = ctx.eval("rustSubtract(10, 3)").unwrap();
println!("Subtract result: {}", subtract_result); // 输出: 7
// 示例4: 对象操作
let user = Object::new(ctx.clone()).unwrap();
user.set("username", "js_user").unwrap();
user.set("score", 100).unwrap();
ctx.globals().set("user", user).unwrap();
let user_info: String = ctx.eval(r#"
`User ${user.username} has ${user.score} points`
"#).unwrap();
println!("{}", user_info); // 输出: User js_user has 100 points
// 示例5: 异步操作
let promise: Promise<String> = ctx.eval(r#"
new Promise((resolve) => {
setTimeout(() => resolve("Async task finished!"), 500);
})
"#).unwrap();
let async_result = block_on(promise).unwrap();
println!("Async result: {}", async_result); // 输出: Async task finished!
// 示例6: 错误处理
match ctx.eval::<(), _>("this.will.throw.error") {
Ok(_) => println!("No error occurred"),
Err(e) => println!("Error occurred: {:?}", e),
}
});
}