• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1{{#title Shared types — Rust ♡ C++}}
2# Shared types
3
4Shared types enable *both* languages to have visibility into the internals of a
5type. This is in contrast to opaque Rust types and opaque C++ types, for which
6only one side gets to manipulate the internals.
7
8Unlike opaque types, the FFI bridge is allowed to pass and return shared types
9by value.
10
11The order in which shared types are written is not important. C++ is order
12sensitive but CXX will topologically sort and forward-declare your types as
13necessary.
14
15## Shared structs and enums
16
17For enums, only C-like a.k.a. unit variants are currently supported.
18
19```rust,noplayground
20#[cxx::bridge]
21mod ffi {
22    struct PlayingCard {
23        suit: Suit,
24        value: u8,  // A=1, J=11, Q=12, K=13
25    }
26
27    enum Suit {
28        Clubs,
29        Diamonds,
30        Hearts,
31        Spades,
32    }
33
34    unsafe extern "C++" {
35        fn deck() -> Vec<PlayingCard>;
36        fn sort(cards: &mut Vec<PlayingCard>);
37    }
38}
39```
40
41## The generated data structures
42
43Shared structs compile to an aggregate-initialization compatible C++ struct.
44
45Shared enums compile to a C++ `enum class` with a sufficiently sized integral
46base type decided by CXX.
47
48```cpp
49// generated header
50
51struct PlayingCard final {
52  Suit suit;
53  uint8_t value;
54};
55
56enum class Suit : uint8_t {
57  Clubs = 0,
58  Diamonds = 1,
59  Hearts = 2,
60  Spades = 3,
61};
62```
63
64Because it is not UB in C++ for an `enum class` to hold a value different from
65all of the listed variants, we use a Rust representation for shared enums that
66is compatible with this. The API you'll get is something like:
67
68```rust,noplayground
69#[derive(Copy, Clone, PartialEq, Eq)]
70#[repr(transparent)]
71pub struct Suit {
72    pub repr: u8,
73}
74#[allow(non_upper_case_globals)]
75impl Suit {
76    pub const Clubs: Self = Suit { repr: 0 };
77    pub const Diamonds: Self = Suit { repr: 1 };
78    pub const Hearts: Self = Suit { repr: 2 };
79    pub const Spades: Self = Suit { repr: 3 };
80}
81```
82
83Notice you're free to treat the enum as an integer in Rust code via the public
84`repr` field.
85
86Pattern matching with `match` still works but will require you to write wildcard
87arms to handle the situation of an enum value that is not one of the listed
88variants.
89
90```rust,noplayground
91fn main() {
92    let suit: Suit = /*...*/;
93    match suit {
94        Suit::Clubs => ...,
95        Suit::Diamonds => ...,
96        Suit::Hearts => ...,
97        Suit::Spades => ...,
98        _ => ...,  // fallback arm
99    }
100}
101```
102
103If a shared struct has generic lifetime parameters, the lifetimes are simply not
104represented on the C++ side. C++ code will need care when working with borrowed
105data (as usual in C++).
106
107```rust,noplayground
108#[cxx::bridge]
109mod ffi {
110    struct Borrowed<'a> {
111        flags: &'a [&'a str],
112    }
113}
114```
115
116```cpp
117// generated header
118
119struct Borrowed final {
120  rust::Slice<const rust::Str> flags;
121};
122```
123
124## Enum discriminants
125
126You may provide explicit discriminants for some or all of the enum variants, in
127which case those numbers will be propagated into the generated C++ `enum class`.
128
129```rust,noplayground
130#[cxx::bridge]
131mod ffi {
132    enum SmallPrime {
133        Two = 2,
134        Three = 3,
135        Five = 5,
136        Seven = 7,
137    }
138}
139```
140
141Variants without an explicit discriminant are assigned the previous discriminant
142plus 1. If the first variant has not been given an explicit discriminant, it is
143assigned discriminant 0.
144
145By default CXX represents your enum using the smallest integer type capable of
146fitting all the discriminants (whether explicit or implicit). If you need a
147different representation for reasons, provide a `repr` attribute.
148
149```rust,noplayground
150#[cxx::bridge]
151mod ffi {
152    #[repr(i32)]
153    enum Enum {
154        Zero,
155        One,
156        Five = 5,
157        Six,
158    }
159}
160```
161
162```cpp
163// generated header
164
165enum class Enum : int32_t {
166  Zero = 0,
167  One = 1,
168  Five = 5,
169  Six = 6,
170};
171```
172
173## Extern enums
174
175If you need to interoperate with an already existing enum for which an existing
176C++ definition is the source of truth, make sure that definition is provided by
177some header in the bridge and then declare your enum *additionally* as an extern
178C++ type.
179
180```rust,noplayground
181#[cxx::bridge]
182mod ffi {
183    enum Enum {
184        Yes,
185        No,
186    }
187
188    extern "C++" {
189        include!("path/to/the/header.h");
190        type Enum;
191    }
192}
193```
194
195CXX will recognize this pattern and, instead of generating a C++ definition of
196the enum, will instead generate C++ static assertions asserting that the
197variants and discriminant values and integer representation written in Rust all
198correctly match the existing C++ enum definition.
199
200Extern enums support all the same features as ordinary shared enums (explicit
201discriminants, repr). Again, CXX will static assert that all of those things you
202wrote are correct.
203
204## Derives
205
206The following standard traits are supported in `derive(...)` within the CXX
207bridge module.
208
209- `Clone`
210- `Copy`
211- `Debug`
212- `Default`
213- `Eq`
214- `Hash`
215- `Ord`
216- `PartialEq`
217- `PartialOrd`
218
219Note that shared enums automatically always come with impls of `Copy`, `Clone`,
220`Eq`, and `PartialEq`, so you're free to omit those derives on an enum.
221
222```rust,noplayground
223#[cxx::bridge]
224mod ffi {
225    #[derive(Clone, Debug, Hash)]
226    struct ExampleStruct {
227        x: u32,
228        s: String,
229    }
230
231    #[derive(Hash, Ord, PartialOrd)]
232    enum ExampleEnum {
233        Yes,
234        No,
235    }
236}
237```
238
239The derives naturally apply to *both* the Rust data type *and* the corresponding
240C++ data type:
241
242- `Hash` gives you a specialization of [`template <> struct std::hash<T>`][hash] in C++
243- `PartialEq` produces `operator==` and `operator!=`
244- `PartialOrd` produces `operator<`, `operator<=`, `operator>`, `operator>=`
245
246[hash]: https://en.cppreference.com/w/cpp/utility/hash
247