Skip to main content
The Deno 2 Release Candidate is here
Learn more

Announcing Stable V8 Bindings for Rust

Deno is a modern, zero-config JavaScript runtime written in Rust. At its core is Rusty V8, a library that provides high-quality, zero-overhead Rust bindings to V8’s C++ API. Over the past five years, Rusty V8 has undergone nearly 150 releases, racking up more than 3.1 million downloads on crates.io. Today, we’re excited to announce a major milestone: Rusty V8 is now stable and production-ready.

While we’d love to mark this moment with a “1.0” release, Rusty V8 is skipping version 1.0 entirely. Instead, we’re aligning with Chrome’s versioning scheme to stay in sync with V8. The first stable release of Rusty V8 will be version 129.0.0, in line with Chrome 129. This version guarantees API stability, moving us beyond years of 0.x releases, and solidifies Rusty V8 as a dependable tool for developers building on top of V8.

What Makes Rusty V8 Special?

Rusty V8 gives Rust developers direct, zero-overhead access to V8’s C++ API. It stands out for its completeness and seamless integration with high-performance environments. A key feature is Rusty V8’s automatic integration of V8’s complex build system into Cargo, allowing developers to easily embed V8 in Rust projects without manual setup. With Rusty V8, you can:

  • Build custom JavaScript runtimes: Rusty V8 is ideal for crafting your own JavaScript runtime, whether for embedded devices, serverless environments, or plugin systems.
  • Run WebAssembly modules: Rusty V8 seamlessly executes WebAssembly (Wasm) modules, allowing you to run high-performance code alongside JavaScript.
  • Leverage the V8 Inspector: Add debugging capabilities like breakpoints and profiling to your JavaScript runtime with the V8 Inspector.
  • Use the V8 Fast API: Rusty V8 allows you to call Rust functions from JavaScript with minimal overhead, perfect for high-performance applications.
  • Automatic memory management: V8’s cppgc garbage collector helps manage memory efficiently, reducing the need for manual memory handling in complex applications.

Origin Story

The journey to Rusty V8 started in 2015, when I was experimenting with building a JavaScript runtime in Go using a library called v8worker. Eventually, v8worker was used in the first Deno demo. However, as Deno evolved, it became clear that Go wasn’t the right fit for the project because of worries that Go’s own GC would conflict poorly with V8’s.

In late 2019, Deno co-founder Bert Belder led the effort to create a direct Rust binding to V8. The challenge was to bind V8’s complex C++ API to Rust without compromising on performance or safety. After much effort, Rusty V8 was born—a zero-overhead Rust binding to V8 that provides full control over V8, while ensuring memory safety through Rust’s ownership model.

Using Rusty V8

Here’s a simple example of how you can embed JavaScript in a Rust program using Rusty V8:

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’s memory safety features are a core advantage over the C++ API. For example, in Rust, handles like Local<T> are tied to specific scopes and enforced at compile time. This prevents bugs caused by using or returning invalid handles—something that C++ developers must manage manually. In Rust, if you attempt to use a Local<T> outside its scope, the compiler will catch the mistake.

Here’s a quick example of Rust preventing invalid handle usage:

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.
}

Versioning

To keep in sync with V8, Rusty V8 will follow Chrome’s versioning scheme. The first stable release is version 129.0.0, corresponding to Chrome 129. While V8 does not follow semantic versioning (semver) and can introduce breaking changes even in minor versions, Rusty V8 will follow semver to ensure compatibility and stability for Rust developers.

Every 4 weeks, Rusty V8 will upgrade its V8 dependency and bump its major version. This means breaking changes to the Rusty V8 API will only happen when V8 upgrades, and the version will always reflect the underlying V8 version.

Ready for Production

Rusty V8 is now a stable, production-ready library for building high-performance JavaScript and WebAssembly runtimes in Rust. Whether you’re creating custom JavaScript runtimes, embedding JS in Rust-backed apps, or exploring server-side JavaScript applications, Rusty V8 provides the flexibility of V8 with the safety and performance guarantees of Rust.

Dive deeper by exploring the full documentation at docs.rs/v8.