Fix null reference creation in shared_ptr::from_ptr_and_deleter.

`shared_ptr::from_ptr_and_deleter` documents that `ptr` may be null (matching
C++ `std::shared_ptr<T>(nullptr, deleter)`), but it built the result with
`project_unchecked(delete_on_drop, |d| &*d.ptr)`, which creates a `&T` from
`ptr`. For a null `ptr`, that is undefined behavior; in debug builds rustc's
null-pointer check panics there.

Instead, take the control block from the `DeleteOnDrop` allocation with
`into_raw_parts` and pair it with `ptr` via `from_raw_parts`, without ever
forming a reference. A null `ptr` still gets a control block (`use_count() == 1`)
and the deleter is still called with null exactly once, as in C++.

Adds tests for both the non-null and null cases; the null test panicked at the
old `&*d.ptr` before this change.

PiperOrigin-RevId: 999765860
2 files changed
tree: 9eb91b02afda9f4591874e202136d285756af806
  1. .github/
  2. assets/
  3. bazel/
  4. buildenv/
  5. cargo/
  6. cc_bindings_from_rs/
  7. common/
  8. crubit_explorer/
  9. docs/
  10. doxygen/
  11. examples/
  12. features/
  13. lifetime_analysis/
  14. lifetime_annotations/
  15. migrator/
  16. nullability/
  17. patches/
  18. rs_bindings_from_cc/
  19. support/
  20. theme/
  21. .bazelignore
  22. .bazelrc
  23. .bazelversion
  24. .gitignore
  25. book.toml
  26. BUILD
  27. BUILD.gn
  28. Cargo.Bazel.lock
  29. Cargo.lock
  30. Cargo.toml
  31. CMakeLists.txt
  32. CODE_OF_CONDUCT
  33. CONTRIBUTING.md
  34. crubit.gni
  35. LICENSE
  36. MODULE.bazel
  37. MODULE.bazel.lock
  38. README.md
  39. rust-toolchain.toml
README.md

Crubit: C++/Rust Bidirectional Interop Tool

rust workflow GitHub

Crubit is a bidirectional bindings generator for C++ and Rust, with the goal of integrating the C++ and Rust ecosystems.

Status

See the status page for an overview of the current supported features.

Example

{}

{{#tab name=“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;
  }
}

{}

{{#tab name=“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);

{}

{}

Getting Started

We have detailed walkthroughs on how to use C++ from Rust, or Rust from C++, using Crubit, as well as copy-pastable example code. The example code also includes spanshots of what the generated bindings look like.

Test Matrix

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