Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

C++ bindings for Rust structs

A Rust struct is mapped to a C++ class/struct with the same fields. If any field cannot be represented in C++, the struct itself will still have bindings, but the relevant field will be private.

To receive C++ bindings, the struct must be movable in C++. See Movable Types.

Example

Given the following Rust module:

// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#[derive(Default, Clone)]
pub struct Struct {
    pub a: i32,
}

Crubit will generate the following bindings:

// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

// Automatically @generated C++ bindings for the following Rust crate:
// example_crate_golden
// Features: custom_ffi_types, non_unpin_ctor, std_unique_ptr, std_vector,
// supported

// clang-format off
#ifndef THIRD_PARTY_CRUBIT_EXAMPLES_RUST_STRUCT_EXAMPLE_CRATE_GOLDEN
#define THIRD_PARTY_CRUBIT_EXAMPLES_RUST_STRUCT_EXAMPLE_CRATE_GOLDEN

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
#include "support/annotations_internal.h"
#include "support/internal/slot.h"

#include <cstddef>
#include <cstdint>
#include <type_traits>

namespace example_crate {

// Generated from:
// examples/rust/struct/example.rs;l=6
struct CRUBIT_INTERNAL_RUST_TYPE(":: example_crate_golden :: Struct") alignas(4)
    [[clang::trivial_abi]] Struct final {
 public:
  // Default::default
  Struct();

  // No custom `Drop` impl and no custom "drop glue" required
  ~Struct() = default;
  Struct(Struct&&) = default;
  Struct& operator=(Struct&&) = default;

  // Clone::clone
  Struct(const Struct&);

  // Clone::clone_from
  Struct& operator=(const Struct&);

  Struct(::crubit::UnsafeRelocateTag, Struct&& value) {
    memcpy(this, &value, sizeof(value));
  }
  union {
    // Generated from:
    // examples/rust/struct/example.rs;l=7
    std::int32_t a;
  };

 private:
  static void __crubit_field_offset_assertions();
};

static_assert(
    sizeof(Struct) == 4,
    "Verify that ADT layout didn't change since this header got generated");
static_assert(
    alignof(Struct) == 4,
    "Verify that ADT layout didn't change since this header got generated");
namespace __crubit_internal {
extern "C" void __crubit_thunk_default(::example_crate::Struct* __ret_ptr);
}
inline Struct::Struct() { __crubit_internal::__crubit_thunk_default(this); }
static_assert(std::is_trivially_destructible_v<Struct>);
static_assert(std::is_trivially_move_constructible_v<Struct>);
static_assert(std::is_trivially_move_assignable_v<Struct>);
namespace __crubit_internal {
extern "C" void __crubit_thunk_clone(::example_crate::Struct const&,
                                     ::example_crate::Struct* __ret_ptr);
}
namespace __crubit_internal {
extern "C" void __crubit_thunk_clone_ufrom(::example_crate::Struct&,
                                           ::example_crate::Struct const&);
}
inline Struct::Struct(const Struct& other) {
  __crubit_internal::__crubit_thunk_clone(other, this);
}
inline Struct& Struct::operator=(const Struct& other) {
  if (this != &other) {
    __crubit_internal::__crubit_thunk_clone_ufrom(*this, other);
  }
  return *this;
}
inline void Struct::__crubit_field_offset_assertions() {
  static_assert(0 == offsetof(Struct, a));
}
}  // namespace example_crate
#pragma clang diagnostic pop
#endif  // THIRD_PARTY_CRUBIT_EXAMPLES_RUST_STRUCT_EXAMPLE_CRATE_GOLDEN

Fields

The fields on the C++ class are the corresponding Rust types:

  • If the Rust field has primitive type, then the C++ field uses the corresponding C++ type.
  • Similarly, if the Rust field has pointer type, then the C++ field has the corresponding C++ pointer type.
  • If the field has a user-defined type, such as a struct or enum, then the bindings for the function use the bindings for that type.

Unsupported fields

Fields that do not receive bindings are made private, and replaced with an opaque blob of maybe-uninitialized bytes, as well as a comment in the generated source code explaining why the field could not receive bindings. For example, since String is not supported, the space of the object occupied by a String field will instead be this opaque blob of bytes:

#![allow(unused)]
fn main() {
// Rust: `my_field` is some unsupported type, such as `String`
pub my_field: String,
}
// C++: `my_field` becomes `private`, and its type is replaced by bytes.
private: unsigned char my_field[24]

Specifically, the following subobjects are hidden and replaced with opaque blobs:

  • Non-public fields (private or pub(...) fields).
  • Fields that implement Drop.
  • Fields whose type does not have bindings.
  • Fields that have an unrecognized or unsupported attribute.

C++ movable

To receive C++ bindings, the struct must be movable in C++. See Movable Types.