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"
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");
208 println!("cargo:rerun-if-changed=src/main.rs");
209 println!("cargo:rerun-if-changed=src/blobstore.cc");
210 println!("cargo:rerun-if-changed=include/blobstore.h");
216 ***[Cargo-based builds](build/cargo.md)*** for more details about CXX's Cargo
219 ```rust,noplayground
223 cxx_build::bridge("src/main.rs")
224 .file("src/blobstore.cc")
226 .compile("cxx-demo");
230 [build scripts]: https://doc.rust-lang.org/cargo/reference/build-scripts.html
236 cxx-demo$ cargo run
237 Compiling cxx-demo v0.1.0
239 Running `target/debug/cxx-demo`
241 cxx-demo$
244 ## Calling a Rust function from C++
253 During a `put`, we'll make C++ call back into Rust to obtain contiguous chunks
258 ```rust,noplayground
259 // src/main.rs
263 extern "Rust" {
266 fn next_chunk(buf: &mut MultiBuf) -> &[u8];
270 include!("cxx-demo/include/blobstore.h");
274 fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
275 fn put(&self, parts: &mut MultiBuf) -> u64;
284 Any signature having a `self` parameter (the Rust name for C++'s `this`) is
285 considered a method / non-static member function. If there is only one `type` in
290 As usual, now we need to provide Rust definitions of everything declared by the
291 `extern "Rust"` block and a C++ definition of the new signature declared by the
294 ```rust,noplayground
295 // src/main.rs
299 # extern "Rust" {
302 # fn next_chunk(buf: &mut MultiBuf) -> &[u8];
306 # include!("cxx-demo/include/blobstore.h");
310 # fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
311 # fn put(&self, parts: &mut MultiBuf) -> u64;
317 // over some more complex Rust data structure like a rope, or maybe loading
324 pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] {
352 In blobstore.cc we're able to call the Rust `next_chunk` function, exposed to
355 relative path of the Rust source file within the crate, and a `.rs.h` extension.
358 // src/blobstore.cc
360 #include "cxx-demo/include/blobstore.h"
361 #include "cxx-demo/src/main.rs.h"
391 ```rust,noplayground
392 // src/main.rs
396 # extern "Rust" {
399 # fn next_chunk(buf: &mut MultiBuf) -> &[u8];
403 # include!("cxx-demo/include/blobstore.h");
407 # fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
408 # fn put(&self, parts: &mut MultiBuf) -> u64;
416 # pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] {
434 cxx-demo$ cargo run
435 Compiling cxx-demo v0.1.0
437 Running `target/debug/cxx-demo`
448 CXX comprises *two* code generators: a Rust one (which is the cxx::bridge
451 ### Rust generated code
454 [cargo-expand]. Then run `cargo expand ::ffi` to macro-expand the `mod ffi`
457 [cargo-expand]: https://github.com/dtolnay/cargo-expand
460 cxx-demo$ cargo install cargo-expand
461 cxx-demo$ cargo expand ::ffi
473 cxx-demo$ exa -T target/cxxbridge/
475 ├── cxx-demo
476 │ └── src
477 │ ├── main.rs.cc -> ../../../debug/build/cxx-demo-11c6f678ce5c3437/out/cxxbridge/sources/cxx-de…
478 │ └── main.rs.h -> ../../../debug/build/cxx-demo-11c6f678ce5c3437/out/cxxbridge/include/cxx-dem…
479 └── rust
480 └── cxx.h -> ~/.cargo/registry/src/github.com-1ecc6299db9ec823/cxx-1.0.0/include/cxx.h
483 In those files you'll see declarations or templates of any CXX Rust types
484 present in your language boundary (like `rust::Slice<T>` for `&[T]`) and `extern
491 cxx-demo$ cargo install cxxbridge-cmd
492 cxx-demo$ cxxbridge src/main.rs
502 boundary. Shared structs translate to a C++ aggregate-initialization compatible
503 struct exactly matching the layout of the Rust one.
506 metadata about blobs between our Rust application and C++ blobstore client.
508 ```rust,noplayground
509 // src/main.rs
518 extern "Rust" {
522 # fn next_chunk(buf: &mut MultiBuf) -> &[u8];
527 # include!("cxx-demo/include/blobstore.h");
531 # fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
532 # fn put(&self, parts: &mut MultiBuf) -> u64;
534 fn metadata(&self, blobid: u64) -> BlobMetadata;
542 # pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] {
558 client.tag(blobid, "rust");
570 #include "rust/cxx.h"
580 void tag(uint64_t blobid, rust::Str tag) const;
592 // src/blobstore.cc
594 #include "cxx-demo/include/blobstore.h"
595 #include "cxx-demo/src/main.rs.h"
602 // Toy implementation of an in-memory blobstore.
631 ... impl->blobs[blobid] = {std::move(contents), {}};
636 void BlobstoreClient::tag(uint64_t blobid, rust::Str tag) const {
637 impl->blobs[blobid].tags.emplace(tag);
643 auto blob = impl->blobs.find(blobid);
644 if (blob != impl->blobs.end()) {
645 metadata.size = blob->second.data.size();
646 std::for_each(blob->second.tags.cbegin(), blob->second.tags.cend(),
658 cxx-demo$ cargo run
659 Running `target/debug/cxx-demo`
662 tags = ["rust"]
674 The key contribution of CXX is it gives you Rust–C++ interop in which
675 *all* of the Rust side of the code you write *really* looks like you are just
676 writing normal Rust, and the C++ side *really* looks like you are just writing
686 CXX plays to the strengths of the Rust type system *and* C++ type system *and*
688 Rust background, or the Rust side without a C++ background, will be able to