Rust的QuickJS绑定库rquickjs-core的使用:高效嵌入JavaScript运行时并执行脚本
Rust的QuickJS绑定库rquickjs-core的使用:高效嵌入JavaScript运行时并执行脚本
rquickjs-core是一个高级的QuickJS JavaScript引擎绑定库,旨在提供类似于rlua库的易用性和安全性包装。
QuickJS的主要特点
- 小巧且易于嵌入:只需几个C文件,没有外部依赖
- 快速的解释器,启动时间极短
- 几乎完整的ES2020支持
- 可以将JavaScript源代码编译为不依赖外部库的可执行文件
- 使用引用计数的垃圾回收机制
- 数学扩展:BigDecimal、BigFloat、运算符重载等
- 命令行解释器
- 小型内置标准库
该crate提供的功能
- 与异步Rust的完整集成
- ES6 Promise可以作为Rust futures处理,反之亦然
- 原生支持Tokio和AsyncStd运行时
- Rust和JS之间的灵活数据转换
- 支持用户定义的分配器
- 支持用户定义的模块解析器和加载器
- 支持将JS模块作为字节码嵌入
- 支持延迟调用JS函数
- 完整的ES6类支持
- 使用proc宏的简单绑定
开发状态
该绑定功能完整,基本稳定并可以使用。错误处理是未来可能改变的唯一部分。
完整示例代码
以下是一个使用rquickjs-core嵌入JavaScript运行时并执行脚本的完整示例:
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
// 执行函数调用
let result: String = ctx.eval(r#"
function greet(name) {
return "Hello, " + name + "!";
}
greet("World")
"#).unwrap();
println!("{}", result); // 输出: Hello, World!
// 执行异步代码
let result: String = ctx.eval(r#"
async function asyncGreet(name) {
return "Hello, " + name + "!";
}
await asyncGreet("Async World")
"#).unwrap();
println!("{}", result); // 输出: Hello, Async World!
});
}
扩展示例代码
以下是一个更完整的示例,展示了如何在Rust和JavaScript之间传递复杂数据:
use rquickjs::{Context, Runtime, Object, Function};
fn main() {
// 创建运行时和上下文
let rt = Runtime::new().unwrap();
let ctx = Context::full(&rt).unwrap();
ctx.with(|ctx| {
// 从Rust调用JavaScript函数
let result: i32 = ctx.eval("function add(a, b) { return a + b; }; add(10, 20)").unwrap();
println!("10 + 20 = {}", result); // 输出: 10 + 20 = 30
// 在JavaScript中使用Rust定义的函数
let callback = Function::new(ctx, |name: String| {
format!("Hello from Rust, {}!", name)
}).unwrap();
let global = ctx.globals();
global.set("rustGreet", callback).unwrap();
let result: String = ctx.eval(r#"
rustGreet("JavaScript")
"#).unwrap();
println!("{}", result); // 输出: Hello from Rust, JavaScript!
// 处理复杂对象
let user = Object::new(ctx).unwrap();
user.set("name", "Alice").unwrap();
user.set("age", 30).unwrap();
let global = ctx.globals();
global.set("user", user).unwrap();
let result: String = ctx.eval(r#"
`User ${user.name} is ${user.age} years old`
"#).unwrap();
println!("{}", result); // 输出: User Alice is 30 years old
});
}
安装
在项目目录中运行以下Cargo命令:
cargo add rquickjs-core
或者在Cargo.toml中添加以下行:
rquickjs-core = "0.9.0"
许可证
该库采用MIT许可证授权。
1 回复
Rust的QuickJS绑定库rquickjs-core使用指南
介绍
rquickjs-core是Rust语言对QuickJS JavaScript引擎的高效绑定库,允许在Rust应用程序中嵌入JavaScript运行时并执行脚本。QuickJS是一个轻量级且快速的JavaScript引擎,支持ES2020规范,非常适合需要JavaScript扩展能力的Rust应用场景。
主要特性
- 轻量级且高性能的JavaScript运行时嵌入
- 支持ES2020最新JavaScript特性
- 内存安全且线程安全的Rust绑定
- 支持Rust与JavaScript之间的双向值转换
- 可配置的运行时和上下文选项
使用方法
添加依赖
首先在Cargo.toml中添加依赖:
[dependencies]
rquickjs-core = "0.4"
基本示例
use rquickjs_core::{Context, Runtime};
fn main() {
// 创建运行时和上下文
let runtime = Runtime::new().unwrap();
let context = Context::full(&runtime).unwrap();
// 在上下文中执行JavaScript代码
context.with(|ctx| {
let result: i32 = ctx.eval("1 + 2 + 3").unwrap();
println!("1 + 2 + 3 = {}", result); // 输出: 1 + 2 + 3 = 6
let result: String = ctx.eval("'Hello, ' + 'World!'").unwrap();
println!("{}", result); // 输出: Hello, World!
});
}
调用JavaScript函数
use rquickjs_core::{Context, Runtime};
fn main() {
let runtime = Runtime::new().unwrap();
let context = Context::full(&runtime).unwrap();
context.with(|ctx| {
// 定义一个JavaScript函数
ctx.eval::<(), _>(r#"
function add(a, b) {
return a + b;
}
"#).unwrap();
// 从Rust调用JavaScript函数
let add_fn: rquickjs_core::Function = ctx.globals().get("add").unwrap();
let result: i32 = add_fn.call((5, 极客时间7)).unwrap();
println!("5 + 7 = {}", result); // 输出: 5 + 7 = 12
});
}
暴露Rust函数给JavaScript
use rquickjs_core::{Context, Runtime, function};
fn main() {
let runtime = Runtime::new().unwrap();
let context = Context::full(&runtime).unwrap();
context.with(|ctx| {
// 定义一个Rust函数并暴露给JavaScript
ctx.globals().set("multiply", function!(|a: i32, b: i32| a * b)).unwrap();
// 在JavaScript中调用Rust函数
let result: i32 = ctx.eval("multiply(3, 4)").unwrap();
println!("3 * 4 = {}", result); // 输出: 3 * 4 = 12
});
}
处理复杂对象
use rquickjs_core::{Context, Runtime};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct Person {
name: String,
age: u8,
}
fn main() {
let runtime = Runtime::new().unwrap();
let context = Context::full(&runtime).unwrap();
context.with(|ctx| {
// 将Rust结构体传递给JavaScript
let person = Person {
name: "Alice".to_string(),
age: 30,
};
ctx.globals().set("person", person).unwrap();
// 在JavaScript中处理对象
let result: String = ctx.eval(r#"
`Name: ${person.name}, Age: ${person.age}`
"#).unwrap();
println!("{}极客时间, result); // 输出: Name: Alice, Age: 30
// 从JavaScript获取对象到Rust
let person: Person = ctx.eval(r#"
({name: "Bob", age: 25})
"#).unwrap();
println!("{:?}", person); // 输出: Person { name: "Bob", age: 25 }
});
}
高级配置
自定义运行时配置
use rquickjs_core::{Runtime, RuntimeOptions};
fn main() {
// 配置运行时选项
let runtime = Runtime::with_options(
RuntimeOptions::default()
.memory_limit(1024 * 1024 * 10) // 10MB内存限制
.stack_size(1024 * 1024) // 1MB栈大小
).unwrap();
// ... 使用运行时 ...
}
模块系统
use rquickjs_core::{Context, Module, Runtime};
fn main() {
let runtime = Runtime::new().unwrap();
let context = Context::full(&runtime).unwrap();
context.with(|ctx| {
// 定义一个模块
let module = Module::new(ctx.clone(), "math", r#"
export function square(x) {
return x * x;
}
export const PI = 3.14159;
"#).unwrap();
// 导入模块
let math = module.eval().unwrap();
// 使用模块中的导出项
let square_fn: rquickjs_core::Function = math.get("square").unwrap();
let pi: f64 = math.get("PI").unwrap();
let result = square_fn.call((5,)).unwrap();
println!("5 squared is {}", result); // 输出: 5 squared is 25
println!("PI is {}", pi); // 输出: PI is 3.14159
});
}
完整示例
下面是一个完整的rquickjs-core使用示例,演示了从创建运行时、执行脚本到双向交互的全过程:
use rquickjs_core::{Context, Runtime, function, Module};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct User {
id: u32,
username: String,
is_admin: bool,
}
fn main() {
// 1. 创建配置好的运行时
let runtime = Runtime::new().expect("Failed to create runtime");
// 2. 创建上下文
let context = Context::full(&runtime).expect("Failed to create context");
context.with(|ctx| {
// 3. 暴露Rust函数给JavaScript
ctx.globals()
.set("log_rust", function!(|msg: String| println!("[Rust]: {}", msg)))
.unwrap();
// 4. 定义JavaScript函数
ctx.eval::<(), _>(r#"
function greet(name) {
return `Hello, ${name}!`;
}
function processUser(user) {
return {
...user,
processed: true,
newField: "Added by JS"
};
}
"#).unwrap();
// 5. 调用JavaScript函数
let greet_fn: rquickjs_core::Function = ctx.globals().get("greet").unwrap();
let greeting: String = greet_fn.call(("World",)).unwrap();
println!("{}", greeting); // 输出: Hello, World!
// 6. 使用模块系统
let module = Module::new(ctx.clone(), "utils", r#"
export function double(x) {
return x * 2;
}
export function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
"#).unwrap();
let utils = module.eval().unwrap();
let double_fn: rquickjs_core::Function = utils.get("double").unwrap();
let result: i32 = double_fn.call((21,)).unwrap();
println!("21 doubled is {}", result); // 输出: 21 doubled is 42
// 7. 处理复杂对象
let user = User {
id: 1,
username: "alice".to_string(),
is_admin: true,
};
ctx.globals().set("user", &user).unwrap();
// 在JavaScript中处理对象
let processed_user: User = ctx.eval(r#"
processUser(user)
"#).unwrap();
println!("Processed user: {:?}", processed_user);
// 8. 从JavaScript调用Rust函数
ctx.eval::<(), _>(r#"
log_rust("This message comes from JavaScript!");
"#).unwrap();
});
}
注意事项
- QuickJS是单线程的,确保不要在多线程中共享运行时或上下文
- 内存管理由Rust的所有权系统处理,但仍需注意循环引用
- 错误处理应使用Rust的Result机制
- 性能敏感场景应考虑减少Rust和JavaScript之间的值转换
rquickjs-core为Rust应用提供了强大的JavaScript集成能力,适用于脚本扩展、配置解析、插件系统等多种场景。