Skip to content

Commit 445dcc6

Browse files
authored
Merge pull request #333 from dtolnay/kind
Update documentation and API of Trivial and Opaque kind markers
2 parents 9f69230 + 38670b9 commit 445dcc6

File tree

3 files changed

+70
-45
lines changed

3 files changed

+70
-45
lines changed

macro/src/expand.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream {
183183

184184
unsafe impl ::cxx::ExternType for #ident {
185185
type Id = #type_id;
186-
type Kind = ::cxx::Opaque;
186+
type Kind = ::cxx::kind::Opaque;
187187
}
188188
}
189189
}
@@ -692,7 +692,7 @@ fn expand_type_alias_kind_trivial_verify(type_alias: &TypeAlias) -> TokenStream
692692
let end = quote_spanned!(end_span=> >);
693693

694694
quote! {
695-
const _: fn() = #begin #ident, ::cxx::Trivial #end;
695+
const _: fn() = #begin #ident, ::cxx::kind::Trivial #end;
696696
}
697697
}
698698

src/extern_type.rs

Lines changed: 67 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use self::kind::{Kind, Opaque, Trivial};
2+
13
/// A type for which the layout is determined by its C++ definition.
24
///
35
/// This trait serves the following two related purposes.
@@ -69,11 +71,11 @@
6971
/// # pub struct StringPiece([usize; 2]);
7072
/// # }
7173
///
72-
/// use cxx::{type_id, ExternType, Opaque};
74+
/// use cxx::{type_id, ExternType};
7375
///
7476
/// unsafe impl ExternType for folly_sys::StringPiece {
7577
/// type Id = type_id!("folly::StringPiece");
76-
/// type Kind = Opaque;
78+
/// type Kind = cxx::kind::Opaque;
7779
/// }
7880
///
7981
/// #[cxx::bridge(namespace = folly)]
@@ -93,29 +95,6 @@
9395
/// #
9496
/// # fn main() {}
9597
/// ```
96-
///
97-
/// ## Opaque and Trivial types
98-
///
99-
/// Some C++ types are safe to hold and pass around in Rust, by value.
100-
/// Those C++ types must have a trivial move constructor, and must
101-
/// have no destructor.
102-
///
103-
/// If you believe your C++ type is indeed trivial, you can specify
104-
/// ```
105-
/// # struct TypeName;
106-
/// # unsafe impl cxx::ExternType for TypeName {
107-
/// type Id = cxx::type_id!("name::space::of::TypeName");
108-
/// type Kind = cxx::Trivial;
109-
/// # }
110-
/// ```
111-
/// which will enable you to pass it into C++ functions by value,
112-
/// return it by value from such functions, and include it in
113-
/// `struct`s that you have declared to `cxx::bridge`. Your promises
114-
/// about the triviality of the C++ type will be checked using
115-
/// `static_assert`s in the generated C++.
116-
///
117-
/// Opaque types can't be passed by value, but can still be held
118-
/// in `UniquePtr`.
11998
pub unsafe trait ExternType {
12099
/// A type-level representation of the type's C++ namespace and type name.
121100
///
@@ -125,32 +104,80 @@ pub unsafe trait ExternType {
125104
/// # struct TypeName;
126105
/// # unsafe impl cxx::ExternType for TypeName {
127106
/// type Id = cxx::type_id!("name::space::of::TypeName");
128-
/// type Kind = cxx::Opaque;
107+
/// # type Kind = cxx::kind::Opaque;
129108
/// # }
130109
/// ```
131110
type Id;
132111

133-
/// Either `cxx::Opaque` or `cxx::Trivial`. If in doubt, use
134-
/// `cxx::Opaque`.
135-
type Kind;
112+
/// Either [`cxx::kind::Opaque`] or [`cxx::kind::Trivial`].
113+
///
114+
/// [`cxx::kind::Opaque`]: kind::Opaque
115+
/// [`cxx::kind::Trivial`]: kind::Trivial
116+
///
117+
/// A C++ type is only okay to hold and pass around by value in Rust if its
118+
/// [move constructor is trivial] and it has no destructor. In CXX, these
119+
/// are called Trivial extern C++ types, while types with nontrivial move
120+
/// behavior or a destructor must be considered Opaque and handled by Rust
121+
/// only behind an indirection, such as a reference or UniquePtr.
122+
///
123+
/// [move constructor is trivial]: https://en.cppreference.com/w/cpp/types/is_move_constructible
124+
///
125+
/// If you believe your C++ type reflected by this ExternType impl is indeed
126+
/// trivial, you can specify:
127+
///
128+
/// ```
129+
/// # struct TypeName;
130+
/// # unsafe impl cxx::ExternType for TypeName {
131+
/// # type Id = cxx::type_id!("name::space::of::TypeName");
132+
/// type Kind = cxx::kind::Trivial;
133+
/// # }
134+
/// ```
135+
///
136+
/// which will enable you to pass it into C++ functions by value, return it
137+
/// by value, and include it in `struct`s that you have declared to
138+
/// `cxx::bridge`. Your claim about the triviality of the C++ type will be
139+
/// checked by a `static_assert` in the generated C++ side of the binding.
140+
type Kind: Kind;
136141
}
137142

138-
pub(crate) mod kind {
143+
/// Marker types identifying Rust's knowledge about an extern C++ type.
144+
///
145+
/// These markers are used in the [`Kind`][ExternType::Kind] associated type in
146+
/// impls of the `ExternType` trait. Refer to the documentation of `Kind` for an
147+
/// overview of their purpose.
148+
pub mod kind {
149+
use super::private;
150+
151+
/// An opaque type which cannot be passed or held by value within Rust.
152+
///
153+
/// Rust's move semantics are such that every move is equivalent to a
154+
/// memcpy. This is incompatible in general with C++'s constructor-based
155+
/// move semantics, so a C++ type which has a destructor or nontrivial move
156+
/// constructor must never exist by value in Rust. In CXX, such types are
157+
/// called opaque C++ types.
158+
///
159+
/// When passed across an FFI boundary, an opaque C++ type must be behind an
160+
/// indirection such as a reference or UniquePtr.
161+
pub enum Opaque {}
162+
163+
/// A type with trivial move constructor and no destructor, which can
164+
/// therefore be owned and moved around in Rust code without requiring
165+
/// indirection.
166+
pub enum Trivial {}
139167

140-
/// An opaque type which can't be passed or held by value within Rust.
141-
/// For example, a C++ type with a destructor, or a non-trivial move
142-
/// constructor. Rust's strict move semantics mean that we can't own
143-
/// these by value in Rust, but they can still be owned by a
144-
/// `UniquePtr`...
145-
pub struct Opaque;
168+
pub trait Kind: private::Sealed {}
169+
impl Kind for Opaque {}
170+
impl Kind for Trivial {}
171+
}
146172

147-
/// A type with trivial move constructors and no destructor, which
148-
/// can therefore be owned and moved around in Rust code directly.
149-
pub struct Trivial;
173+
mod private {
174+
pub trait Sealed {}
175+
impl Sealed for super::Opaque {}
176+
impl Sealed for super::Trivial {}
150177
}
151178

152179
#[doc(hidden)]
153180
pub fn verify_extern_type<T: ExternType<Id = Id>, Id>() {}
154181

155182
#[doc(hidden)]
156-
pub fn verify_extern_kind<T: ExternType<Kind = Kind>, Kind>() {}
183+
pub fn verify_extern_kind<T: ExternType<Kind = Kind>, Kind: self::Kind>() {}

src/lib.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -395,9 +395,7 @@ mod unwind;
395395
pub use crate::cxx_string::CxxString;
396396
pub use crate::cxx_vector::CxxVector;
397397
pub use crate::exception::Exception;
398-
pub use crate::extern_type::kind::Opaque;
399-
pub use crate::extern_type::kind::Trivial;
400-
pub use crate::extern_type::ExternType;
398+
pub use crate::extern_type::{kind, ExternType};
401399
pub use crate::unique_ptr::UniquePtr;
402400
pub use cxxbridge_macro::bridge;
403401

0 commit comments

Comments
 (0)