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();
    });
}

注意事项

  1. QuickJS是单线程的,确保不要在多线程中共享运行时或上下文
  2. 内存管理由Rust的所有权系统处理,但仍需注意循环引用
  3. 错误处理应使用Rust的Result机制
  4. 性能敏感场景应考虑减少Rust和JavaScript之间的值转换

rquickjs-core为Rust应用提供了强大的JavaScript集成能力,适用于脚本扩展、配置解析、插件系统等多种场景。

回到顶部