Public API:

// rust/cxx.h

#include <type_traits>

namespace rust {

template <typename T>
class Box final {
  using element_type = T;
  using const_pointer =
      typename std::add_pointer<typename std::add_const<T>::type>::type;
  using pointer = typename std::add_pointer<T>::type;

  Box(Box &&) noexcept;
  ~Box() noexcept;

  explicit Box(const T &);
  explicit Box(T &&);

  Box &operator=(Box &&) noexcept;

  const T *operator->() const noexcept;
  const T &operator*() const noexcept;
  T *operator->() noexcept;
  T &operator*() noexcept;

  template <typename... Fields>
  static Box in_place(Fields &&...);

  void swap(Box &) noexcept;

  // Important: requires that `raw` came from an into_raw call. Do not
  // pass a pointer from `new` or any other source.
  static Box from_raw(T *) noexcept;

  T *into_raw() noexcept;

} // namespace rust


Box<T> does not support T being an opaque C++ type. You should use UniquePtr<T> or SharedPtr<T> instead for transferring ownership of opaque C++ types on the language boundary.

If T is an opaque Rust type, the Rust type is required to be Sized i.e. size known at compile time. In the future we may introduce support for dynamically sized opaque Rust types.


This program uses a Box to pass ownership of some opaque piece of Rust state over to C++ and then back to a Rust callback, which is a useful pattern for implementing async functions over FFI.

// src/

use std::io::Write;

mod ffi {
    extern "Rust" {
        type File;

    unsafe extern "C++" {

        fn f(
            callback: fn(Box<File>, fst: &str, snd: &str),
            out: Box<File>,

pub struct File(std::fs::File);

fn main() {
    let out = std::fs::File::create("example.log").unwrap();

        |mut out, fst, snd| { let _ = write!(out.0, "{}{}\n", fst, snd); },
// include/example.h

#pragma once
#include "example/src/"
#include "rust/cxx.h"

void f(rust::Fn<void(rust::Box<File>, rust::Str, rust::Str)> callback,
       rust::Box<File> out);
// include/

#include "example/include/example.h"

void f(rust::Fn<void(rust::Box<File>, rust::Str, rust::Str)> callback,
       rust::Box<File> out) {
  callback(std::move(out), "fearless", "concurrency");