跳转至主要内容

宣布 Rust 稳定的 V8 绑定

Deno 是一个现代的、零配置的 JavaScript 运行时,用 Rust 编写。它的核心是 Rusty V8,一个为 V8 的 C++ API 提供高质量、零开销 Rust 绑定的库。在过去的五年里,Rusty V8 经历了近 150 个版本发布,在 crates.io 上积累了超过 310 万次的下载量。今天,我们激动地宣布一个重要的里程碑:Rusty V8 现在已稳定且可用于生产环境

虽然我们很想用 “1.0” 版本来标记这一时刻,但 Rusty V8 完全跳过了 1.0 版本。相反,我们正在与 Chrome 的版本方案保持一致,以便与 V8 同步。Rusty V8 的第一个稳定版本将是 129.0.0 版本,与 Chrome 129 版本一致。此版本保证了 API 的稳定性,使我们摆脱了多年的 0.x 版本,并将 Rusty V8 巩固为开发者在 V8 之上构建应用时可以信赖的工具。

是什么让 Rusty V8 与众不同?

Rusty V8 为 Rust 开发者提供了直接、零开销地访问 V8 的 C++ API 的能力。它因其完整性以及与高性能环境的无缝集成而脱颖而出。一个关键特性是 Rusty V8 自动将 V8 复杂的构建系统集成到 Cargo 中,使得开发者可以轻松地将 V8 嵌入到 Rust 项目中,而无需手动设置。使用 Rusty V8,你可以

  • 构建自定义 JavaScript 运行时:Rusty V8 非常适合制作你自己的 JavaScript 运行时,无论是用于嵌入式设备、无服务器环境还是插件系统。
  • 运行 WebAssembly 模块:Rusty V8 无缝执行 WebAssembly (Wasm) 模块,允许你并行运行高性能代码和 JavaScript。
  • 利用 V8 Inspector:通过 V8 Inspector 为你的 JavaScript 运行时添加断点和性能分析等调试功能。
  • 使用 V8 Fast API:Rusty V8 允许你以最小的开销从 JavaScript 调用 Rust 函数,非常适合高性能应用程序。
  • 自动内存管理:V8 的 cppgc 垃圾回收器有助于高效地管理内存,减少了在复杂应用程序中手动处理内存的需求。

起源故事

Rusty V8 的旅程始于 2015 年,当时我正在尝试使用一个名为 v8worker 的库在 Go 中构建 JavaScript 运行时。最终,v8worker 被用于第一个 Deno 演示。然而,随着 Deno 的发展,很明显 Go 并不适合这个项目,因为人们担心 Go 自己的 GC 会与 V8 的 GC 产生不良冲突。

在 2019 年末,Deno 联合创始人 Bert Belder 领导了创建 V8 的直接 Rust 绑定的工作。挑战在于将 V8 复杂的 C++ API 绑定到 Rust,同时不损害性能或安全性。经过大量努力,Rusty V8 诞生了——一个零开销的 V8 Rust 绑定,它提供了对 V8 的完全控制,同时通过 Rust 的所有权模型确保内存安全。

使用 Rusty V8

这是一个简单的示例,说明如何使用 Rusty V8 将 JavaScript 嵌入到 Rust 程序中

fn main() {
  // Initialize V8.
  let platform = v8::new_default_platform(0, false).make_shared();
  v8::V8::initialize_platform(platform);
  v8::V8::initialize();

  // Create a new Isolate and make it the current one.
  let isolate = &mut v8::Isolate::new(v8::CreateParams::default());

  // Create a stack-allocated handle scope.
  let handle_scope = &mut v8::HandleScope::new(isolate);

  // Create a new context.
  let context = v8::Context::new(handle_scope, Default::default());

  // Enter the context for compiling and running the hello world script.
  let scope = &mut v8::ContextScope::new(handle_scope, context);

  // Create a string containing the JavaScript source code.
  let code = v8::String::new(scope, "'Hello' + ' World!'").unwrap();

  // Compile the source code.
  let script = v8::Script::compile(scope, code, None).unwrap();

  // Run the script to get the result.
  let result = script.run(scope).unwrap();

  // Convert the result to a string and print it.
  let result = result.to_string(scope).unwrap();
  println!("{}", result.to_rust_string_lossy(scope));
}

Rusty V8 的内存安全特性是相对于 C++ API 的核心优势。例如,在 Rust 中,像 Local<T> 这样的句柄与特定的作用域绑定,并在编译时强制执行。这可以防止因使用或返回无效句柄而引起的错误——C++ 开发者必须手动管理这些错误。在 Rust 中,如果你尝试在作用域外使用 Local<T>,编译器会捕获这个错误。

这是一个 Rust 防止无效句柄使用的快速示例

let isolate = &mut v8::Isolate::new(Default::default());
{
  let scope1 = &mut v8::HandleScope::new(isolate);
  let local = v8::Integer::new(scope1, 123);

  // Attempting to use 'local' outside the scope will fail at compile time
  let invalid_local = {
    let scope2 = &mut v8::HandleScope::new(scope1);
    v8::Integer::new(scope2, 456) // Safe to use in this scope
  };

  // Rust will not allow 'invalid_local' to be accessed here.
}

版本控制

为了与 V8 保持同步,Rusty V8 将遵循 Chrome 的版本控制方案。第一个稳定版本是 129.0.0 版本,对应于 Chrome 129。虽然 V8 不遵循语义版本控制 (semver),并且即使在小版本中也可能引入破坏性更改,但 Rusty V8 将遵循 semver,以确保 Rust 开发者的兼容性和稳定性。

每 4 周,Rusty V8 将升级其 V8 依赖项并增加其主版本号。这意味着对 Rusty V8 API 的破坏性更改只会发生在 V8 升级时,并且版本将始终反映底层 V8 版本。

可用于生产环境

Rusty V8 现在是一个稳定、可用于生产环境的库,用于在 Rust 中构建高性能 JavaScript 和 WebAssembly 运行时。无论你是创建自定义 JavaScript 运行时、在 Rust 支持的应用程序中嵌入 JS,还是探索服务器端 JavaScript 应用程序,Rusty V8 都能提供 V8 的灵活性以及 Rust 的安全性和性能保证。

通过访问 docs.rs/v8 上的完整文档深入了解更多信息。