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