133d722a9Sopenharmony_ci{{#title Other Rust–C++ interop tools — Rust ♡ C++}}
233d722a9Sopenharmony_ci# Context: other Rust–C++ interop tools
333d722a9Sopenharmony_ci
433d722a9Sopenharmony_ciWhen it comes to interacting with an idiomatic Rust API or idiomatic C++ API
533d722a9Sopenharmony_cifrom the other language, the generally applicable approaches outside of the CXX
633d722a9Sopenharmony_cicrate are:
733d722a9Sopenharmony_ci
833d722a9Sopenharmony_ci- Build a C-compatible wrapper around the code (expressed using `extern "C"`
933d722a9Sopenharmony_ci  signatures, primitives, C-compatible structs, raw pointers). Translate that
1033d722a9Sopenharmony_ci  manually to equivalent `extern "C"` declarations in the other language and
1133d722a9Sopenharmony_ci  keep them in sync. Preferably, build a safe/idiomatic wrapper around the
1233d722a9Sopenharmony_ci  translated `extern "C"` signatures for callers to use.
1333d722a9Sopenharmony_ci
1433d722a9Sopenharmony_ci- Build a C wrapper around the C++ code and use **[bindgen]** to translate that
1533d722a9Sopenharmony_ci  programmatically to `extern "C"` Rust signatures. Preferably, build a
1633d722a9Sopenharmony_ci  safe/idiomatic Rust wrapper on top.
1733d722a9Sopenharmony_ci
1833d722a9Sopenharmony_ci- Build a C-compatible Rust wrapper around the Rust code and use **[cbindgen]**
1933d722a9Sopenharmony_ci  to translate that programmatically to an `extern "C"` C++ header. Preferably,
2033d722a9Sopenharmony_ci  build an idiomatic C++ wrapper.
2133d722a9Sopenharmony_ci
2233d722a9Sopenharmony_ci**If the code you are binding is already *"effectively C"*, the above has you
2333d722a9Sopenharmony_cicovered.** You should use bindgen or cbindgen, or manually translated C
2433d722a9Sopenharmony_cisignatures if there aren't too many and they seldom change.
2533d722a9Sopenharmony_ci
2633d722a9Sopenharmony_ci[bindgen]: https://github.com/rust-lang/rust-bindgen
2733d722a9Sopenharmony_ci[cbindgen]: https://github.com/eqrion/cbindgen
2833d722a9Sopenharmony_ci
2933d722a9Sopenharmony_ci## C++ vs C
3033d722a9Sopenharmony_ci
3133d722a9Sopenharmony_ciBindgen has some basic support for C++. It can reason about classes, member
3233d722a9Sopenharmony_cifunctions, and the layout of templated types. However, everything it does
3333d722a9Sopenharmony_cirelated to C++ is best-effort only. Bindgen starts from a point of wanting to
3433d722a9Sopenharmony_cigenerate declarations for everything, so any C++ detail that it hasn't
3533d722a9Sopenharmony_ciimplemented will cause a crash if you are lucky ([bindgen#388]) or more likely
3633d722a9Sopenharmony_cisilently emit an incompatible signature ([bindgen#380], [bindgen#607],
3733d722a9Sopenharmony_ci[bindgen#652], [bindgen#778], [bindgen#1194]) which will do arbitrary
3833d722a9Sopenharmony_cimemory-unsafe things at runtime whenever called.
3933d722a9Sopenharmony_ci
4033d722a9Sopenharmony_ci[bindgen#388]: https://github.com/rust-lang/rust-bindgen/issues/388
4133d722a9Sopenharmony_ci[bindgen#380]: https://github.com/rust-lang/rust-bindgen/issues/380
4233d722a9Sopenharmony_ci[bindgen#607]: https://github.com/rust-lang/rust-bindgen/issues/607
4333d722a9Sopenharmony_ci[bindgen#652]: https://github.com/rust-lang/rust-bindgen/issues/652
4433d722a9Sopenharmony_ci[bindgen#778]: https://github.com/rust-lang/rust-bindgen/issues/778
4533d722a9Sopenharmony_ci[bindgen#1194]: https://github.com/rust-lang/rust-bindgen/issues/1194
4633d722a9Sopenharmony_ci
4733d722a9Sopenharmony_ciThus using bindgen correctly requires not just juggling all your pointers
4833d722a9Sopenharmony_cicorrectly at the language boundary, but also understanding ABI details and their
4933d722a9Sopenharmony_ciworkarounds and reliably applying them. For example, the programmer will
5033d722a9Sopenharmony_cidiscover that their program sometimes segfaults if they call a function that
5133d722a9Sopenharmony_cireturns std::unique\_ptr\<T\> through bindgen. Why? Because unique\_ptr, despite
5233d722a9Sopenharmony_cibeing "just a pointer", has a different ABI than a pointer or a C struct
5333d722a9Sopenharmony_cicontaining a pointer ([bindgen#778]) and is not directly expressible in Rust.
5433d722a9Sopenharmony_ciBindgen emitted something that *looks* reasonable and you will have a hell of a
5533d722a9Sopenharmony_citime in gdb working out what went wrong. Eventually people learn to avoid
5633d722a9Sopenharmony_cianything involving a non-trivial copy constructor, destructor, or inheritance,
5733d722a9Sopenharmony_ciand instead stick to raw pointers and primitives and trivial structs only
5833d722a9Sopenharmony_ci&mdash; in other words C.
5933d722a9Sopenharmony_ci
6033d722a9Sopenharmony_ci## Geometric intuition for why there is so much opportunity for improvement
6133d722a9Sopenharmony_ci
6233d722a9Sopenharmony_ciThe CXX project attempts a different approach to C++ FFI.
6333d722a9Sopenharmony_ci
6433d722a9Sopenharmony_ciImagine Rust and C and C++ as three vertices of a scalene triangle, with length
6533d722a9Sopenharmony_ciof the edges being related to similarity of the languages when it comes to
6633d722a9Sopenharmony_cilibrary design.
6733d722a9Sopenharmony_ci
6833d722a9Sopenharmony_ciThe most similar pair (the shortest edge) is Rust&ndash;C++. These languages
6933d722a9Sopenharmony_cihave largely compatible concepts of things like ownership, vectors, strings,
7033d722a9Sopenharmony_cifallibility, etc that translate clearly from signatures in either language to
7133d722a9Sopenharmony_cisignatures in the other language.
7233d722a9Sopenharmony_ci
7333d722a9Sopenharmony_ciWhen we make a binding for an idiomatic C++ API using bindgen, and we fall down
7433d722a9Sopenharmony_cito raw pointers and primitives and trivial structs as described above, what we
7533d722a9Sopenharmony_ciare really doing is coding the two longest edges of the triangle: getting from
7633d722a9Sopenharmony_ciC++ down to C, and C back up to Rust. The Rust&ndash;C edge always involves a
7733d722a9Sopenharmony_cigreat deal of `unsafe` code, and the C++&ndash;C edge similarly requires care
7833d722a9Sopenharmony_cijust for basic memory safety. Something as basic as "how do I pass ownership of
7933d722a9Sopenharmony_cia string to the other language?" becomes a strap-yourself-in moment,
8033d722a9Sopenharmony_ciparticularly for someone not already an expert in one or both sides.
8133d722a9Sopenharmony_ci
8233d722a9Sopenharmony_ciYou should think of the `cxx` crate as being the midpoint of the Rust&ndash;C++
8333d722a9Sopenharmony_ciedge. Rather than coding the two long edges, you will code half the short edge
8433d722a9Sopenharmony_ciin Rust and half the short edge in C++, in both cases with the library playing
8533d722a9Sopenharmony_cito the strengths of the Rust type system *and* the C++ type system to help
8633d722a9Sopenharmony_ciassure correctness.
8733d722a9Sopenharmony_ci
8833d722a9Sopenharmony_ciIf you've already been through the tutorial in the previous chapter, take a
8933d722a9Sopenharmony_cimoment to appreciate that the C++ side *really* looks like we are just writing
9033d722a9Sopenharmony_ciC++ and the Rust side *really* looks like we are just writing Rust. Anything you
9133d722a9Sopenharmony_cicould do wrong in Rust, and almost anything you could reasonably do wrong in
9233d722a9Sopenharmony_ciC++, will be caught by the compiler. This highlights that we are on the "short
9333d722a9Sopenharmony_ciedge of the triangle".
9433d722a9Sopenharmony_ci
9533d722a9Sopenharmony_ciBut it all still boils down to the same things: it's still FFI from one piece of
9633d722a9Sopenharmony_cinative code to another, nothing is getting serialized or allocated or
9733d722a9Sopenharmony_ciruntime-checked in between.
9833d722a9Sopenharmony_ci
9933d722a9Sopenharmony_ci## Role of CXX
10033d722a9Sopenharmony_ci
10133d722a9Sopenharmony_ciThe role of CXX is to capture the language boundary with more fidelity than what
10233d722a9Sopenharmony_ci`extern "C"` is able to represent. You can think of CXX as being a replacement
10333d722a9Sopenharmony_cifor `extern "C"` in a sense.
10433d722a9Sopenharmony_ci
10533d722a9Sopenharmony_ciFrom this perspective, CXX is a lower level tool than the bindgens. Just as
10633d722a9Sopenharmony_cibindgen and cbindgen are built on top of `extern "C"`, it makes sense to think
10733d722a9Sopenharmony_ciabout higher level tools built on top of CXX. Such a tool might consume a C++
10833d722a9Sopenharmony_ciheader and/or Rust module (and/or IDL like Thrift) and emit the corresponding
10933d722a9Sopenharmony_cisafe cxx::bridge language boundary, leveraging CXX's static analysis and
11033d722a9Sopenharmony_ciunderlying implementation of that boundary. We are beginning to see this space
11133d722a9Sopenharmony_ciexplored by the [autocxx] tool, though nothing yet ready for broad use in the
11233d722a9Sopenharmony_ciway that CXX on its own is.
11333d722a9Sopenharmony_ci
11433d722a9Sopenharmony_ci[autocxx]: https://github.com/google/autocxx
11533d722a9Sopenharmony_ci
11633d722a9Sopenharmony_ciBut note in other ways CXX is higher level than the bindgens, with rich support
11733d722a9Sopenharmony_cifor common standard library types. CXX's types serve as an intuitive vocabulary
11833d722a9Sopenharmony_cifor designing a good boundary between components in different languages.
119