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"
386 ```rust,noplayground
387 // src/main.rs
391 # extern "Rust" {
394 # fn next_chunk(buf: &mut MultiBuf) -> &[u8];
398 # include!("cxx-demo/include/blobstore.h");
402 # fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
403 # fn put(&self, parts: &mut MultiBuf) -> u64;
411 # pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] {
429 cxx-demo$ cargo run
430 Compiling cxx-demo v0.1.0
432 Running `target/debug/cxx-demo`
443 CXX comprises *two* code generators: a Rust one (which is the cxx::bridge
446 ### Rust generated code
449 [cargo-expand]. Then run `cargo expand ::ffi` to macro-expand the `mod ffi`
452 [cargo-expand]: https://github.com/dtolnay/cargo-expand
455 cxx-demo$ cargo install cargo-expand
456 cxx-demo$ cargo expand ::ffi
468 cxx-demo$ exa -T target/cxxbridge/
470 ├── cxx-demo
471 │ └── src
472 │ ├── main.rs.cc -> ../../../debug/build/cxx-demo-11c6f678ce5c3437/out/cxxbridge/sources/cxx-de…
473 │ └── main.rs.h -> ../../../debug/build/cxx-demo-11c6f678ce5c3437/out/cxxbridge/include/cxx-dem…
474 └── rust
475 └── cxx.h -> ~/.cargo/registry/src/github.com-1ecc6299db9ec823/cxx-1.0.0/include/cxx.h
478 In those files you'll see declarations or templates of any CXX Rust types
479 present in your language boundary (like `rust::Slice<T>` for `&[T]`) and `extern
486 cxx-demo$ cargo install cxxbridge-cmd
487 cxx-demo$ cxxbridge src/main.rs
497 boundary. Shared structs translate to a C++ aggregate-initialization compatible
498 struct exactly matching the layout of the Rust one.
501 metadata about blobs between our Rust application and C++ blobstore client.
503 ```rust,noplayground
504 // src/main.rs
513 extern "Rust" {
517 # fn next_chunk(buf: &mut MultiBuf) -> &[u8];
522 # include!("cxx-demo/include/blobstore.h");
526 # fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
527 # fn put(&self, parts: &mut MultiBuf) -> u64;
529 fn metadata(&self, blobid: u64) -> BlobMetadata;
537 # pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] {
553 client.tag(blobid, "rust");
565 # #include "rust/cxx.h"
575 void tag(uint64_t blobid, rust::Str tag) const;
587 // src/blobstore.cc
589 ##include "cxx-demo/include/blobstore.h"
590 ##include "cxx-demo/src/main.rs.h"
597 // Toy implementation of an in-memory blobstore.
626 # impl->blobs[blobid] = {std::move(contents), {}};
631 void BlobstoreClient::tag(uint64_t blobid, rust::Str tag) const {
632 impl->blobs[blobid].tags.emplace(tag);
638 auto blob = impl->blobs.find(blobid);
639 if (blob != impl->blobs.end()) {
640 metadata.size = blob->second.data.size();
641 std::for_each(blob->second.tags.cbegin(), blob->second.tags.cend(),
653 cxx-demo$ cargo run
654 Running `target/debug/cxx-demo`
657 tags = ["rust"]
669 The key contribution of CXX is it gives you Rust–C++ interop in which
670 *all* of the Rust side of the code you write *really* looks like you are just
671 writing normal Rust, and the C++ side *really* looks like you are just writing
681 CXX plays to the strengths of the Rust type system *and* C++ type system *and*
683 Rust background, or the Rust side without a C++ background, will be able to