Clone this repo:
  1. 356f531 Update Crubit build system docs to focus on what works outside Google. by Taylor Cramer · 23 minutes ago upstream/main
  2. 1596fe7 Refresh Crubit documentation for overview. by Googler · 30 minutes ago
  3. f9d1854 Document and test absl::Span and std::span support for exported &[T] parameters. by Taylor Cramer · 37 minutes ago
  4. 406356b Move leaf helpers so cc_bindings_from_rs's generate_bindings modules form a DAG by Taylor Cramer · 50 minutes ago
  5. 74437aa Propagate `crate_features` into generated `BUILD.gn` files. by Lukasz Anforowicz · 61 minutes ago

Crubit: C++/Rust Bidirectional Interop Tool

rust workflow GitHub

Crubit is a bidirectional bindings generator for C++ and Rust, allowing users to:

  • Automatically reuse existing crates and headers: no IDL or glue code is necessary.
  • Call back and forth between Rust and C++ with no performance cost: no copies or heap allocations required.
  • Use idiomatic types from both Rust and C++, such as Vec, Result, Option, std::vector, std::unique_ptr, std::shared_ptr, and more!

Example

Calling Rust from C++

Consider the following Rust library:

pub struct Account {
    pub id: u64,
    pub balance: f64,
}

impl std::fmt::Display for Account { ... }

// Takes shared references, mapped to const references in C++.
pub fn calculate_interest(account: &Account, rate: f64) -> f64 { ... }

// Takes a string slice, mapped to rs_std::StrRef in C++.
pub fn is_valid_username(username: &str) -> bool { ... }

You can call these Rust functions from C++:

#include "path/to/account.h"
#include <iostream>

void demo() {
  account::Account my_account{.id = 123, .balance = 1000.0};
  double interest = account::calculate_interest(my_account, 0.05);

  // my_account is printable because Account implements Display in Rust!
  std::cout << my_account << std::endl;

  if (account::is_valid_username("bob")) {
    std::cout << "Valid user" << std::endl;
  }
}

Calling C++ from Rust

Consider the following C++ header:

#include <string_view>
#include <optional>
#include <memory>

struct User {
  int id;
  double balance;
};

std::optional<User> FindUser(int id);
std::unique_ptr<User> CreateUser(std::string_view name, int id);

You can call these C++ functions from Rust:

use cpp_std::unique_ptr;
use ffi_11::c_int;
use user_api::{CreateUser, FindUser, User};

let id: c_int = 123;
let user: Option<User> = FindUser(id);
if let Some(u) = user {
    let balance: f64 = u.balance;
    println!("User {} has balance {}", u.id, balance);
}

let new_user: unique_ptr<User> = CreateUser("Alice".into(), 456);

Why Crubit?

  • No hand-written glue code. Crubit reads C++ headers and Rust crates directly and generates bindings in both directions. Your signatures can‘t drift out of sync with the code they wrap, and you don’t need to maintain a separate interface file.
  • More than the C ABI. Crubit can call functions that aren‘t extern "C", pass structs and enums by value, and match the memory layout of Rust structs and enums that aren’t #[repr(C)].
  • Idiomatic types on both sides. References, slices, &str, Option, Result, tuples, smart pointers, std::vector/Vec, callbacks, absl::Status, and more map to their natural counterparts. See the status page for the full table.
  • Traits and operators, too. C++ can call Rust trait implementations, and Rust traits such as Display, Clone, and Default become the matching C++ operators and special member functions. In the other direction, C++ operator overloads and special member functions become the matching Rust traits, such as PartialEq, Add, Clone, and Drop.
  • Safer than hand-written FFI. Crubit generates bindings from the real C++ headers and Rust crates at build time, with the same compiler and flags, so mismatched ABIs, struct layouts, and signatures can't creep in. C++ references get Rust lifetimes through lifetime elision and lifetime annotations, so the Rust borrow checker can check them. Slices, strings, Option, and other types carry their bounds and invariants across the boundary instead of degrading to raw pointers. See Memory Safety for the details.

Getting Started

1. Set up your build

Crubit runs as part of your build, so that the bindings always match the code they wrap. Pick your build system:

Build systemC++ calling Rust (cpp_api_from_rust)Rust calling C++ (rust_api_from_cpp)
BazelSupported (example)Supported
CMakeSupported (example)Not yet
OtherThrough the cargo cpp_api_from_rust subcommandNot yet

Crubit works with nightly Rust toolchains and with the two most recent stable releases, as long as the rustc-dev component is installed (see the test matrix below). There are no binary releases yet; pin a commit of this repository. Build System Integrations covers toolchain requirements and how to set up each build system.

2. Use your Rust and C++ APIs together

Add a cpp_api_from_rust or rust_api_from_cpp target next to your library, and start calling it from the other language. Each direction has a walkthrough that explains what the generated API looks like and how to customize it, plus copy-pastable examples that include snapshots of the generated bindings:

Test Matrix

NightlyStable
nightly-0stable-0
nightly-1stable-1
nightly-2
nightly-3
nightly-4
nightly-5
nightly-6