133d722a9Sopenharmony_ci{{#title Result<T> — Rust ♡ C++}}
233d722a9Sopenharmony_ci# Result\<T\>
333d722a9Sopenharmony_ci
433d722a9Sopenharmony_ciResult\<T\> is allowed as the return type of an extern function in either
533d722a9Sopenharmony_cidirection. Its behavior is to translate to/from C++ exceptions. If your codebase
633d722a9Sopenharmony_cidoes not use C++ exceptions, or prefers to represent fallibility using something
733d722a9Sopenharmony_cilike outcome\<T\>, leaf::result\<T\>, StatusOr\<T\>, etc then you'll need to
833d722a9Sopenharmony_cihandle the translation of those to Rust Result\<T\> using your own shims for
933d722a9Sopenharmony_cinow. Better support for this is planned.
1033d722a9Sopenharmony_ci
1133d722a9Sopenharmony_ciIf an exception is thrown from an `extern "C++"` function that is *not* declared
1233d722a9Sopenharmony_ciby the CXX bridge to return Result, the program calls C++'s `std::terminate`.
1333d722a9Sopenharmony_ciThe behavior is equivalent to the same exception being thrown through a
1433d722a9Sopenharmony_ci`noexcept` C++ function.
1533d722a9Sopenharmony_ci
1633d722a9Sopenharmony_ciIf a panic occurs in *any* `extern "Rust"` function, regardless of whether it is
1733d722a9Sopenharmony_cideclared by the CXX bridge to return Result, a message is logged and the program
1833d722a9Sopenharmony_cicalls Rust's `std::process::abort`.
1933d722a9Sopenharmony_ci
2033d722a9Sopenharmony_ci## Returning Result from Rust to C++
2133d722a9Sopenharmony_ci
2233d722a9Sopenharmony_ciAn `extern "Rust"` function returning a Result turns into a `throw` in C++ if
2333d722a9Sopenharmony_cithe Rust side produces an error.
2433d722a9Sopenharmony_ci
2533d722a9Sopenharmony_ciNote that the return type written inside of cxx::bridge must be written without
2633d722a9Sopenharmony_cia second type parameter. Only the Ok type is specified for the purpose of the
2733d722a9Sopenharmony_ciFFI. The Rust *implementation* (outside of the bridge module) may pick any error
2833d722a9Sopenharmony_citype as long as it has a std::fmt::Display impl.
2933d722a9Sopenharmony_ci
3033d722a9Sopenharmony_ci```rust,noplayground
3133d722a9Sopenharmony_ci# use std::io;
3233d722a9Sopenharmony_ci#
3333d722a9Sopenharmony_ci#[cxx::bridge]
3433d722a9Sopenharmony_cimod ffi {
3533d722a9Sopenharmony_ci    extern "Rust" {
3633d722a9Sopenharmony_ci        fn fallible1(depth: usize) -> Result<String>;
3733d722a9Sopenharmony_ci        fn fallible2() -> Result<()>;
3833d722a9Sopenharmony_ci    }
3933d722a9Sopenharmony_ci}
4033d722a9Sopenharmony_ci
4133d722a9Sopenharmony_cifn fallible1(depth: usize) -> anyhow::Result<String> {
4233d722a9Sopenharmony_ci    if depth == 0 {
4333d722a9Sopenharmony_ci        return Err(anyhow::Error::msg("fallible1 requires depth > 0"));
4433d722a9Sopenharmony_ci    }
4533d722a9Sopenharmony_ci    ...
4633d722a9Sopenharmony_ci}
4733d722a9Sopenharmony_ci
4833d722a9Sopenharmony_cifn fallible2() -> Result<(), io::Error> {
4933d722a9Sopenharmony_ci    ...
5033d722a9Sopenharmony_ci    Ok(())
5133d722a9Sopenharmony_ci}
5233d722a9Sopenharmony_ci```
5333d722a9Sopenharmony_ci
5433d722a9Sopenharmony_ciThe exception that gets thrown by CXX on the C++ side is always of type
5533d722a9Sopenharmony_ci`rust::Error` and has the following C++ public API. The `what()` member function
5633d722a9Sopenharmony_cigives the error message according to the Rust error's std::fmt::Display impl.
5733d722a9Sopenharmony_ci
5833d722a9Sopenharmony_ci```cpp,hidelines
5933d722a9Sopenharmony_ci// rust/cxx.h
6033d722a9Sopenharmony_ci#
6133d722a9Sopenharmony_ci# namespace rust {
6233d722a9Sopenharmony_ci
6333d722a9Sopenharmony_ciclass Error final : public std::exception {
6433d722a9Sopenharmony_cipublic:
6533d722a9Sopenharmony_ci  Error(const Error &);
6633d722a9Sopenharmony_ci  Error(Error &&) noexcept;
6733d722a9Sopenharmony_ci  ~Error() noexcept;
6833d722a9Sopenharmony_ci
6933d722a9Sopenharmony_ci  Error &operator=(const Error &);
7033d722a9Sopenharmony_ci  Error &operator=(Error &&) noexcept;
7133d722a9Sopenharmony_ci
7233d722a9Sopenharmony_ci  const char *what() const noexcept override;
7333d722a9Sopenharmony_ci};
7433d722a9Sopenharmony_ci#
7533d722a9Sopenharmony_ci# } // namespace rust
7633d722a9Sopenharmony_ci```
7733d722a9Sopenharmony_ci
7833d722a9Sopenharmony_ci## Returning Result from C++ to Rust
7933d722a9Sopenharmony_ci
8033d722a9Sopenharmony_ciAn `extern "C++"` function returning a Result turns into a `catch` in C++ that
8133d722a9Sopenharmony_ciconverts the exception into an Err for Rust.
8233d722a9Sopenharmony_ci
8333d722a9Sopenharmony_ciNote that the return type written inside of cxx::bridge must be written without
8433d722a9Sopenharmony_cia second type parameter. Only the Ok type is specified for the purpose of the
8533d722a9Sopenharmony_ciFFI. The resulting error type created by CXX when an `extern "C++"` function
8633d722a9Sopenharmony_cithrows will always be of type **[`cxx::Exception`]**.
8733d722a9Sopenharmony_ci
8833d722a9Sopenharmony_ci[`cxx::Exception`]: https://docs.rs/cxx/*/cxx/struct.Exception.html
8933d722a9Sopenharmony_ci
9033d722a9Sopenharmony_ci```rust,noplayground
9133d722a9Sopenharmony_ci# use std::process;
9233d722a9Sopenharmony_ci#
9333d722a9Sopenharmony_ci#[cxx::bridge]
9433d722a9Sopenharmony_cimod ffi {
9533d722a9Sopenharmony_ci    unsafe extern "C++" {
9633d722a9Sopenharmony_ci        include!("example/include/example.h");
9733d722a9Sopenharmony_ci        fn fallible1(depth: usize) -> Result<String>;
9833d722a9Sopenharmony_ci        fn fallible2() -> Result<()>;
9933d722a9Sopenharmony_ci    }
10033d722a9Sopenharmony_ci}
10133d722a9Sopenharmony_ci
10233d722a9Sopenharmony_cifn main() {
10333d722a9Sopenharmony_ci    if let Err(err) = ffi::fallible1(99) {
10433d722a9Sopenharmony_ci        eprintln!("Error: {}", err);
10533d722a9Sopenharmony_ci        process::exit(1);
10633d722a9Sopenharmony_ci    }
10733d722a9Sopenharmony_ci}
10833d722a9Sopenharmony_ci```
10933d722a9Sopenharmony_ci
11033d722a9Sopenharmony_ciThe specific set of caught exceptions and the conversion to error message are
11133d722a9Sopenharmony_ciboth customizable. The way you do this is by defining a template function
11233d722a9Sopenharmony_ci`rust::behavior::trycatch` with a suitable signature inside any one of the
11333d722a9Sopenharmony_ciheaders `include!`'d by your cxx::bridge.
11433d722a9Sopenharmony_ci
11533d722a9Sopenharmony_ciThe template signature is required to be:
11633d722a9Sopenharmony_ci
11733d722a9Sopenharmony_ci```cpp,hidelines
11833d722a9Sopenharmony_cinamespace rust {
11933d722a9Sopenharmony_cinamespace behavior {
12033d722a9Sopenharmony_ci
12133d722a9Sopenharmony_citemplate <typename Try, typename Fail>
12233d722a9Sopenharmony_cistatic void trycatch(Try &&func, Fail &&fail) noexcept;
12333d722a9Sopenharmony_ci
12433d722a9Sopenharmony_ci} // namespace behavior
12533d722a9Sopenharmony_ci} // namespace rust
12633d722a9Sopenharmony_ci```
12733d722a9Sopenharmony_ci
12833d722a9Sopenharmony_ciThe default `trycatch` used by CXX if you have not provided your own is the
12933d722a9Sopenharmony_cifollowing. You must follow the same pattern: invoke `func` with no arguments,
13033d722a9Sopenharmony_cicatch whatever exception(s) you want, and invoke `fail` with the error message
13133d722a9Sopenharmony_ciyou'd like for the Rust error to have.
13233d722a9Sopenharmony_ci
13333d722a9Sopenharmony_ci```cpp,hidelines
13433d722a9Sopenharmony_ci# #include <exception>
13533d722a9Sopenharmony_ci#
13633d722a9Sopenharmony_ci# namespace rust {
13733d722a9Sopenharmony_ci# namespace behavior {
13833d722a9Sopenharmony_ci#
13933d722a9Sopenharmony_citemplate <typename Try, typename Fail>
14033d722a9Sopenharmony_cistatic void trycatch(Try &&func, Fail &&fail) noexcept try {
14133d722a9Sopenharmony_ci  func();
14233d722a9Sopenharmony_ci} catch (const std::exception &e) {
14333d722a9Sopenharmony_ci  fail(e.what());
14433d722a9Sopenharmony_ci}
14533d722a9Sopenharmony_ci#
14633d722a9Sopenharmony_ci# } // namespace behavior
14733d722a9Sopenharmony_ci# } // namespace rust
14833d722a9Sopenharmony_ci```
149