Trait cxx::ExternType

source ·
pub unsafe trait ExternType {
    type Id;
    type Kind: Kind;
}
Expand description

A type for which the layout is determined by its C++ definition.

This trait serves the following two related purposes.


Safely unifying occurrences of the same extern type

ExternType makes it possible for CXX to safely share a consistent Rust type across multiple #[cxx::bridge] invocations that refer to a common extern C++ type.

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. The CXX code generator will use an automatically generated ExternType impl emitted in file1 to statically verify that in file2 crate::file1::ffi::Demo really does refer to the C++ type example::Demo as expected in file2.

// 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 types

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.

Required Associated Types§

A type-level representation of the type’s C++ namespace and type name.

This will always be defined using type_id! in the following form:

type Id = cxx::type_id!("name::space::of::TypeName");

Either cxx::kind::Opaque or cxx::kind::Trivial.

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 this ExternType impl is indeed fine to hold by value and move in Rust, you can specify:

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.

Implementations on Foreign Types§

Implementors§