extern "C++"

#[cxx::bridge]
mod ffi {
    extern "C++" {
        include!("path/to/header.h");
        include!("path/to/another.h");

        ...
    }
}

The extern "C++" section of a CXX bridge declares C++ types and signatures to be made available to Rust, and gives the paths of the header(s) which contain the corresponding C++ declarations.

A bridge module may contain zero or more extern "C++" blocks.

Opaque C++ types

Type defined in C++ that are made available to Rust, but only behind an indirection.

#[cxx::bridge]
mod ffi {
    extern "C++" {
        include!("path/to/header.h");
        
        type MyType;
        type MyOtherType;
    }
}

For example in the Tutorial we saw BlobstoreClient implemented as an opaque C++ type. The blobstore client was created in C++ and returned to Rust by way of a UniquePtr.

Mutability: Unlike extern Rust types and shared types, an extern C++ type is not permitted to be passed by plain mutable reference &mut MyType across the FFI bridge. For mutation support, the bridge is required to use Pin<&mut MyType>. This is to safeguard against things like mem::swap-ing the contents of two mutable references, given that Rust doesn't have information about the size of the underlying object and couldn't invoke an appropriate C++ move constructor anyway.

Thread safety: Be aware that CXX does not assume anything about the thread safety of your extern C++ types. In other words the MyType etc bindings which CXX produces for you in Rust do not come with Send and Sync impls. If you are sure that your C++ type satisfies the requirements of Send and/or Sync and need to leverage that fact from Rust, you must provide your own unsafe marker trait impls.

#[cxx::bridge]
mod ffi {
    extern "C++" {
        include!("path/to/header.h");

        type MyType;
    }
}

/// The C++ implementation of MyType is thread safe.
unsafe impl Send for ffi::MyType {}
unsafe impl Sync for ffi::MyType {}

Take care in doing this because thread safety in C++ can be extremely tricky to assess if you are coming from a Rust background. For example the BlobstoreClient type in the tutorial is not thread safe despite doing only completely innocuous things in its implementation. Concurrent calls to the tag member function trigger a data race on the blobs map.

Functions and member functions

This largely follows the same principles as extern "Rust" functions and methods. In particular, any signature with a self parameter is interpreted as a C++ non-static member function and exposed to Rust as a method.

The programmer does not need to promise that the signatures they have typed in are accurate; that would be unreasonable. CXX performs static assertions that the signatures exactly correspond with what is declared in C++. Rather, the programmer is only on the hook for things that C++'s static information is not precise enough to capture, i.e. things that would only be represented at most by comments in the C++ code unintelligible to a static assertion: namely whether the C++ function is safe or unsafe to be called from Rust.

Safety: the extern "C++" block is responsible for deciding whether to expose each signature inside as safe-to-call or unsafe-to-call. If an extern block contains at least one safe-to-call signature, it must be written as an unsafe extern block, which serves as an item level unsafe block to indicate that an unchecked safety claim is being made about the contents of the block.

#[cxx::bridge]
mod ffi {
    unsafe extern "C++" {
        include!("path/to/header.h");
        
        fn f();  // safe to call
    }

    extern "C++" {
        unsafe fn g();  // unsafe to call
    }
}

Lifetimes

C++ types holding borrowed data may be described naturally in Rust by an extern type with a generic lifetime parameter. For example in the case of the following pair of types:

// header.h

class Resource;

class TypeContainingBorrow {
  TypeContainingBorrow(const Resource &res) : res(res) {}
  const Resource &res;
};

std::shared_ptr<TypeContainingBorrow> create(const Resource &res);

we'd want to expose this to Rust as:

#[cxx::bridge]
mod ffi {
    unsafe extern "C++" {
        include!("path/to/header.h");
        
        type Resource;
        type TypeContainingBorrow<'a>;

        fn create<'a>(res: &'a Resource) -> SharedPtr<TypeContainingBorrow<'a>>;

        // or with lifetime elision:
        fn create(res: &Resource) -> SharedPtr<TypeContainingBorrow>;
    }
}

Reusing existing binding types

Extern C++ types support a syntax for declaring that a Rust binding of the correct C++ type already exists outside of the current bridge module. This avoids generating a fresh new binding which Rust's type system would consider non-interchangeable with the first.

#[cxx::bridge(namespace = "path::to")]
mod ffi {
    extern "C++" {
        type MyType = crate::existing::MyType;
    }

    extern "Rust" {
        fn f(x: &MyType) -> usize;
    }
}

In this case rather than producing a unique new Rust type ffi::MyType for the Rust binding of C++'s ::path::to::MyType, CXX will reuse the already existing binding at crate::existing::MyType in expressing the signature of f and any other uses of MyType within the bridge module.

CXX safely validates that crate::existing::MyType is in fact a binding for the right C++ type ::path::to::MyType by generating a static assertion based on crate::existing::MyType's implementation of ExternType, which is a trait automatically implemented by CXX for bindings that it generates but can also be manually implemented as described below.

ExternType serves the following two related use cases.

Safely unifying occurrences of an extern type across bridges

In the following snippet, two #[cxx::bridge] invocations in different files (possibly different crates) both contain function signatures involving the same C++ type example::Demo. If both were written just containing type Demo;, then both macro expansions would produce their own separate Rust type called Demo and thus the compiler wouldn't allow us to take the Demo returned by file1::ffi::create_demo and pass it as the Demo argument accepted by file2::ffi::take_ref_demo. Instead, one of the two Demos has been defined as an extern type alias of the other, making them the same type in Rust.

// file1.rs
#[cxx::bridge(namespace = "example")]
pub mod ffi {
    unsafe extern "C++" {
        type Demo;

        fn create_demo() -> UniquePtr<Demo>;
    }
}
// file2.rs
#[cxx::bridge(namespace = "example")]
pub mod ffi {
    unsafe extern "C++" {
        type Demo = crate::file1::ffi::Demo;

        fn take_ref_demo(demo: &Demo);
    }
}

Integrating with bindgen-generated or handwritten unsafe bindings

Handwritten ExternType impls make it possible to plug in a data structure emitted by bindgen as the definition of a C++ type emitted by CXX.

By writing the unsafe ExternType impl, the programmer asserts that the C++ namespace and type name given in the type id refers to a C++ type that is equivalent to Rust type that is the Self type of the impl.

mod folly_sys;  // the bindgen-generated bindings

use cxx::{type_id, ExternType};

unsafe impl ExternType for folly_sys::StringPiece {
    type Id = type_id!("folly::StringPiece");
    type Kind = cxx::kind::Opaque;
}

#[cxx::bridge(namespace = "folly")]
pub mod ffi {
    unsafe extern "C++" {
        include!("rust_cxx_bindings.h");

        type StringPiece = crate::folly_sys::StringPiece;

        fn print_string_piece(s: &StringPiece);
    }
}

// Now if we construct a StringPiece or obtain one through one
// of the bindgen-generated signatures, we are able to pass it
// along to ffi::print_string_piece.

The ExternType::Id associated type encodes a type-level representation of the type's C++ namespace and type name. It will always be defined using the type_id! macro exposed in the cxx crate.

The ExternType::Kind associated type will always be either cxx::kind::Opaque or cxx::kind::Trivial identifying whether a C++ type is soundly relocatable by Rust's move semantics. A C++ type is only okay to hold and pass around by value in Rust if its move constructor is trivial and it has no destructor. In CXX, these are called Trivial extern C++ types, while types with nontrivial move behavior or a destructor must be considered Opaque and handled by Rust only behind an indirection, such as a reference or UniquePtr.

If you believe your C++ type reflected by the ExternType impl is indeed fine to hold by value and move in Rust, you can specify:

unsafe impl cxx::ExternType for TypeName {
    type Id = cxx::type_id!("name::space::of::TypeName");
    type Kind = cxx::kind::Trivial;
}

which will enable you to pass it into C++ functions by value, return it by value, and include it in structs that you have declared to cxx::bridge. Your claim about the triviality of the C++ type will be checked by a static_assert in the generated C++ side of the binding.

Explicit shim trait impls

This is a somewhat niche feature, but important when you need it.

CXX's support for C++'s std::unique_ptr and std::vector is built on a set of internal trait impls connecting the Rust API of UniquePtr and CxxVector to underlying template instantiations performed by the C++ compiler.

When reusing a binding type across multiple bridge modules as described in the previous section, you may find that your code needs some trait impls which CXX hasn't decided to generate.

#[cxx::bridge]
mod ffi1 {
    extern "C++" {
        include!("path/to/header.h");

        type A;
        type B;

        // Okay: CXX sees UniquePtr<B> using a type B defined within the same
        // bridge, and automatically emits the right template instantiations
        // corresponding to std::unique_ptr<B>.
        fn get_b() -> UniquePtr<B>;
    }
}

#[cxx::bridge]
mod ffi2 {
    extern "C++" {
        type A = crate::ffi1::A;

        // Rust trait error: CXX processing this module has no visibility into
        // whether template instantiations corresponding to std::unique_ptr<A>
        // have already been emitted by the upstream library, so it does not
        // emit them here. If the upstream library does not have any signatures
        // involving UniquePtr<A>, an explicit instantiation of the template
        // needs to be requested in one module or the other.
        fn get_a() -> UniquePtr<A>;
    }
}

You can request a specific template instantiation at a particular location in the Rust crate hierarchy by writing impl UniquePtr<A> {} inside of the bridge module which defines A but does not otherwise contain any use of UniquePtr<A>.

#[cxx::bridge]
mod ffi1 {
    extern "C++" {
        include!("path/to/header.h");

        type A;
        type B;

        fn get_b() -> UniquePtr<B>;
    }

    impl UniquePtr<A> {}  // explicit instantiation
}