Rust in WebRTC

Current status

WebRTC is adopting Rust to advance memory safety. Rust targets and build infrastructure have been integrated into the repository and are enabled by default.

Currently there are few Rust component integrated into tests, but not production code.

As a next step we plan to integrate a small Rust component into production code, but keep a c++ fallback.

rtc_rust build flag

Rust support is controlled by the GN build argument rtc_rust in webrtc.gni:

# Enables or disables Rust targets in WebRTC.
rtc_rust = true

C++ code can check if Rust is enabled by checking WEBRTC_WITHOUT_RUST define.

We plan to eventually remove the rtc_rust build flag and WEBRTC_WITHOUT_RUST define and require all downstream builds to provide a working Rust toolchain.

GN Templates

  • rtc_rust_library: Defines a Rust static library.
  • rtc_rust_cxx_bridge: Defines a single-source C++/Rust FFI bridge using the CXX crate.
  • rtc_rust_unittest: Defines a single Rust unit test binary.
  • rtc_rust_test_suite: Aggregates multiple Rust unit test targets into a test suite group.

Style guide

With few exceptions, WebRTC follows Chromium Rust Style guide, which in turn follows Rust Style Guide and Rust API Guidelines.

Use webrtc::import! macro

webrtc::import! {
  // Crates can be imported using their target name (e.g. `time_delta_rs`):
  "//api/unit:time_delta_rs";

  // Target names can be overridden for brevity:
  "//modules/rtp_rtcp:corruption_detection_extension" as cde;
}

Use Rust standard test framework

See Rust Book for the guidance.

Support for Chromium's test framework or Googles test framework is currently not implemented.

C++/Rust Interoperability

Interoperability between C++ and Rust is handled through the CXX crate.

We plan to switch to Crubit when its support will be implemented in more environments, in particular when it will be fully supported by chromium infrastructure.

FFI bindings are declared in Rust source files using #[cxx::bridge]. Use C++ namespace webrtc for all bindings. Declare bindings in Rust mod ffi.

Examples:

// bindings to call C++ from Rust

#[cxx::bridge(namespace = "webrtc")]
mod ffi {
    unsafe extern "C++" {
        include!("path/to/a_cpp_library.h");

        fn CppFunction(value: i32) -> i64;
    }
}

pub fn cpp_function(value: i32) -> i64 {
    ffi::CppFunction(value)
}
// bindings to call Rust from C++

webrtc::import! {
    "//path/to/rust:a_rust_library";
}

use a_rust_library::rust_function;

#[cxx::bridge(namespace = "webrtc")]
mod ffi {
    extern "Rust" {
        fn rust_function(value: i64) -> u64;
    }
}
import("../../webrtc.gni")

rtc_library("cpp_only_library") {
  sources = [
    "cpp_only_library.cc",
    "cpp_only_library.h",
  ]
}

if (rtc_rust) {
  rtc_rust_cxx_bridge("call_cpp_from_rust_cxx") {
    allow_unsafe = true
    source = "call_cpp_from_rust_cxx.rs"
    deps = [
      ":cpp_only_library",
      "//build/rust:cxx_rustdeps",
    ]
  }

  rtc_rust_library("my_rust_feature") {
    crate_root = "my_rust_feature.rs"
    sources = [ "my_rust_feature.rs" ]
    deps = [
      ":call_cpp_from_rust_cxx",
    ]
  }

  rtc_rust_unittest("my_rust_feature_test") {
    crate_root = "my_rust_feature.rs"
    sources = [ "my_rust_feature.rs" ]
    deps = [
      ":call_cpp_from_rust_cxx",
    ]
  }

  rtc_rust_cxx_bridge("call_rust_from_cpp_cxx") {
    allow_unsafe = true
    source = "call_rust_from_cpp_cxx.rs"
    deps = [
      ":my_rust_feature",
      "//build/rust:cxx_rustdeps",
    ]
  }
}

rtc_library("cpp_maybe_use_rust") {
  sources = [
    "cpp_maybe_use_rust.cc"
  ]
  deps = [...]

  if (rtc_rust) {
    deps += [":call_rust_from_cpp_cxx"]
  }
}
// cpp_maybe_use_rust.cc

#ifndef WEBRTC_WITHOUT_RUST
#include "call_rust_from_cpp_cxx.rs.h"
#endif

...
uint64_t MaybeUseRust(int64_t value) {
#ifdef WEBRTC_WITHOUT_RUST
  return CallCppFallback(value);
#else
  return rust_function(value);
#endif
}