1{{#title Result<T> — Rust ♡ C++}} 2# Result\<T\> 3 4Result\<T\> is allowed as the return type of an extern function in either 5direction. Its behavior is to translate to/from C++ exceptions. If your codebase 6does not use C++ exceptions, or prefers to represent fallibility using something 7like outcome\<T\>, leaf::result\<T\>, StatusOr\<T\>, etc then you'll need to 8handle the translation of those to Rust Result\<T\> using your own shims for 9now. Better support for this is planned. 10 11If an exception is thrown from an `extern "C++"` function that is *not* declared 12by the CXX bridge to return Result, the program calls C++'s `std::terminate`. 13The behavior is equivalent to the same exception being thrown through a 14`noexcept` C++ function. 15 16If a panic occurs in *any* `extern "Rust"` function, regardless of whether it is 17declared by the CXX bridge to return Result, a message is logged and the program 18calls Rust's `std::process::abort`. 19 20## Returning Result from Rust to C++ 21 22An `extern "Rust"` function returning a Result turns into a `throw` in C++ if 23the Rust side produces an error. 24 25Note that the return type written inside of cxx::bridge must be written without 26a second type parameter. Only the Ok type is specified for the purpose of the 27FFI. The Rust *implementation* (outside of the bridge module) may pick any error 28type as long as it has a std::fmt::Display impl. 29 30```rust,noplayground 31# use std::io; 32# 33#[cxx::bridge] 34mod ffi { 35 extern "Rust" { 36 fn fallible1(depth: usize) -> Result<String>; 37 fn fallible2() -> Result<()>; 38 } 39} 40 41fn fallible1(depth: usize) -> anyhow::Result<String> { 42 if depth == 0 { 43 return Err(anyhow::Error::msg("fallible1 requires depth > 0")); 44 } 45 ... 46} 47 48fn fallible2() -> Result<(), io::Error> { 49 ... 50 Ok(()) 51} 52``` 53 54The exception that gets thrown by CXX on the C++ side is always of type 55`rust::Error` and has the following C++ public API. The `what()` member function 56gives the error message according to the Rust error's std::fmt::Display impl. 57 58```cpp,hidelines 59// rust/cxx.h 60# 61# namespace rust { 62 63class Error final : public std::exception { 64public: 65 Error(const Error &); 66 Error(Error &&) noexcept; 67 ~Error() noexcept; 68 69 Error &operator=(const Error &); 70 Error &operator=(Error &&) noexcept; 71 72 const char *what() const noexcept override; 73}; 74# 75# } // namespace rust 76``` 77 78## Returning Result from C++ to Rust 79 80An `extern "C++"` function returning a Result turns into a `catch` in C++ that 81converts the exception into an Err for Rust. 82 83Note that the return type written inside of cxx::bridge must be written without 84a second type parameter. Only the Ok type is specified for the purpose of the 85FFI. The resulting error type created by CXX when an `extern "C++"` function 86throws will always be of type **[`cxx::Exception`]**. 87 88[`cxx::Exception`]: https://docs.rs/cxx/*/cxx/struct.Exception.html 89 90```rust,noplayground 91# use std::process; 92# 93#[cxx::bridge] 94mod ffi { 95 unsafe extern "C++" { 96 include!("example/include/example.h"); 97 fn fallible1(depth: usize) -> Result<String>; 98 fn fallible2() -> Result<()>; 99 } 100} 101 102fn main() { 103 if let Err(err) = ffi::fallible1(99) { 104 eprintln!("Error: {}", err); 105 process::exit(1); 106 } 107} 108``` 109 110The specific set of caught exceptions and the conversion to error message are 111both customizable. The way you do this is by defining a template function 112`rust::behavior::trycatch` with a suitable signature inside any one of the 113headers `include!`'d by your cxx::bridge. 114 115The template signature is required to be: 116 117```cpp,hidelines 118namespace rust { 119namespace behavior { 120 121template <typename Try, typename Fail> 122static void trycatch(Try &&func, Fail &&fail) noexcept; 123 124} // namespace behavior 125} // namespace rust 126``` 127 128The default `trycatch` used by CXX if you have not provided your own is the 129following. You must follow the same pattern: invoke `func` with no arguments, 130catch whatever exception(s) you want, and invoke `fail` with the error message 131you'd like for the Rust error to have. 132 133```cpp,hidelines 134# #include <exception> 135# 136# namespace rust { 137# namespace behavior { 138# 139template <typename Try, typename Fail> 140static void trycatch(Try &&func, Fail &&fail) noexcept try { 141 func(); 142} catch (const std::exception &e) { 143 fail(e.what()); 144} 145# 146# } // namespace behavior 147# } // namespace rust 148``` 149