Lines Matching +full:rust +full:- +full:src
1 {{#title Tutorial — Rust ♡ C++}}
4 This example walks through a Rust application that calls into a C++ client of a
5 blobstore service. In fact we'll see calls going in both directions: Rust to C++
6 as well as C++ to Rust. For your own use case it may be that you need just one
19 We'll use Cargo, which is the build system commonly used by open source Rust
22 Create a blank Cargo project: `mkdir cxx-demo`; `cd cxx-demo`; `cargo init`.
29 # name = "cxx-demo"
43 in a Rust module annotated with the `#[cxx::bridge]` attribute macro.
45 We'll open with just the following at the top of src/main.rs and walk through
48 ```rust,noplayground
49 // src/main.rs
62 ## Calling a C++ function from Rust
68 that Rust does not need to assume anything about its implementation, not even
69 its size or alignment. In general, a C++ type might have a move-constructor
70 which is incompatible with Rust's move semantics, or may hold internal
71 references which cannot be modeled by Rust's borrowing system. Though there are
76 `&`, a Rust `Box`, or a `UniquePtr` (Rust binding of `std::unique_ptr`). We'll
78 to Rust.
80 ```rust,noplayground
81 // src/main.rs
86 include!("cxx-demo/include/blobstore.h");
90 fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
100 [*extern "C++"*](extern-c++.md) chapter. In brief: the programmer is **not**
107 something like "must be called at most once or we'll stomp yer memery", Rust
120 …= /bin/ld: target/debug/deps/cxx-demo-7cb7fddf3d67d880.rcgu.o: in function `cxx_demo::ffi::new_blo…
121 src/main.rs:1: undefined reference to `cxxbridge1$new_blobstore_client'
130 `include!("cxx-demo/include/blobstore.h")` above — we'll be putting the
131 C++ header at relative path `include/blobstore.h` within the Rust crate. If your
132 crate is named something other than `cxx-demo` according to the `name` field in
133 Cargo.toml, you will need to use that name everywhere in place of `cxx-demo`
151 // src/blobstore.cc
153 #include "cxx-demo/include/blobstore.h"
162 Using `std::make_unique` would work too, as long as you pass `-std=c++14` to the
165 The placement in *include/* and *src/* is not significant; you can place C++
175 Cargo has a [build scripts] feature suitable for compiling non-Rust code.
177 We need to introduce a new build-time dependency on CXX's C++ code generator in
183 # name = "cxx-demo"
190 [build-dependencies]
191 cxx-build = "1.0"
194 Then add a build.rs build script adjacent to Cargo.toml to run the cxx-build
195 code generator and C++ compiler. The relevant arguments are the path to the Rust
197 paths to any additional C++ source files to be compiled during the Rust crate's
200 ```rust,noplayground
204 cxx_build::bridge("src/main.rs")
205 .file("src/blobstore.cc")
206 .compile("cxx-demo");
212 ***[Cargo-based builds](build/cargo.md)*** for more details about CXX's Cargo
215 ```rust,noplayground
219 cxx_build::bridge("src/main.rs")
220 .file("src/blobstore.cc")
221 .flag_if_supported("-std=c++14")
222 .compile("cxx-demo");
226 [build scripts]: https://doc.rust-lang.org/cargo/reference/build-scripts.html
232 cxx-demo$ cargo run
233 Compiling cxx-demo v0.1.0
235 Running `target/debug/cxx-demo`
237 cxx-demo$
240 ## Calling a Rust function from C++
249 During a `put`, we'll make C++ call back into Rust to obtain contiguous chunks
254 ```rust,noplayground
255 // src/main.rs
259 extern "Rust" {
262 fn next_chunk(buf: &mut MultiBuf) -> &[u8];
266 include!("cxx-demo/include/blobstore.h");
270 fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
271 fn put(&self, parts: &mut MultiBuf) -> u64;
280 Any signature having a `self` parameter (the Rust name for C++'s `this`) is
281 considered a method / non-static member function. If there is only one `type` in
286 As usual, now we need to provide Rust definitions of everything declared by the
287 `extern "Rust"` block and a C++ definition of the new signature declared by the
290 ```rust,noplayground
291 // src/main.rs
295 # extern "Rust" {
298 # fn next_chunk(buf: &mut MultiBuf) -> &[u8];
302 # include!("cxx-demo/include/blobstore.h");
306 # fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
307 # fn put(&self, parts: &mut MultiBuf) -> u64;
313 // over some more complex Rust data structure like a rope, or maybe loading
320 pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] {
348 In blobstore.cc we're able to call the Rust `next_chunk` function, exposed to
351 relative path of the Rust source file within the crate, and a `.rs.h` extension.
354 // src/blobstore.cc
356 ##include "cxx-demo/include/blobstore.h"
357 ##include "cxx-demo/src/main.rs.h"
387 ```rust,noplayground
388 // src/main.rs
392 # extern "Rust" {
395 # fn next_chunk(buf: &mut MultiBuf) -> &[u8];
399 # include!("cxx-demo/include/blobstore.h");
403 # fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
404 # fn put(&self, parts: &mut MultiBuf) -> u64;
412 # pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] {
430 cxx-demo$ cargo run
431 Compiling cxx-demo v0.1.0
433 Running `target/debug/cxx-demo`
444 CXX comprises *two* code generators: a Rust one (which is the cxx::bridge
447 ### Rust generated code
450 [cargo-expand]. Then run `cargo expand ::ffi` to macro-expand the `mod ffi`
453 [cargo-expand]: https://github.com/dtolnay/cargo-expand
456 cxx-demo$ cargo install cargo-expand
457 cxx-demo$ cargo expand ::ffi
469 cxx-demo$ exa -T target/cxxbridge/
471 ├── cxx-demo
472 │ └── src
473 │ ├── main.rs.cc -> ../../../debug/build/cxx-demo-11c6f678ce5c3437/out/cxxbridge/sources/cxx-de…
474 │ └── main.rs.h -> ../../../debug/build/cxx-demo-11c6f678ce5c3437/out/cxxbridge/include/cxx-dem…
475 └── rust
476 └── cxx.h -> ~/.cargo/registry/src/github.com-1ecc6299db9ec823/cxx-1.0.0/include/cxx.h
479 In those files you'll see declarations or templates of any CXX Rust types
480 present in your language boundary (like `rust::Slice<T>` for `&[T]`) and `extern
487 cxx-demo$ cargo install cxxbridge-cmd
488 cxx-demo$ cxxbridge src/main.rs
498 boundary. Shared structs translate to a C++ aggregate-initialization compatible
499 struct exactly matching the layout of the Rust one.
502 metadata about blobs between our Rust application and C++ blobstore client.
504 ```rust,noplayground
505 // src/main.rs
514 extern "Rust" {
518 # fn next_chunk(buf: &mut MultiBuf) -> &[u8];
523 # include!("cxx-demo/include/blobstore.h");
527 # fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
528 # fn put(&self, parts: &mut MultiBuf) -> u64;
530 fn metadata(&self, blobid: u64) -> BlobMetadata;
538 # pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] {
554 client.tag(blobid, "rust");
566 ##include "rust/cxx.h"
576 void tag(uint64_t blobid, rust::Str tag) const;
588 // src/blobstore.cc
590 ##include "cxx-demo/include/blobstore.h"
591 ##include "cxx-demo/src/main.rs.h"
598 // Toy implementation of an in-memory blobstore.
627 # impl->blobs[blobid] = {std::move(contents), {}};
632 void BlobstoreClient::tag(uint64_t blobid, rust::Str tag) const {
633 impl->blobs[blobid].tags.emplace(tag);
639 auto blob = impl->blobs.find(blobid);
640 if (blob != impl->blobs.end()) {
641 metadata.size = blob->second.data.size();
642 std::for_each(blob->second.tags.cbegin(), blob->second.tags.cend(),
654 cxx-demo$ cargo run
655 Running `target/debug/cxx-demo`
658 tags = ["rust"]
670 The key contribution of CXX is it gives you Rust–C++ interop in which
671 *all* of the Rust side of the code you write *really* looks like you are just
672 writing normal Rust, and the C++ side *really* looks like you are just writing
682 CXX plays to the strengths of the Rust type system *and* C++ type system *and*
684 Rust background, or the Rust side without a C++ background, will be able to