Throwing errors

It is often the case that a function does not return T in Rust but Result<T, E> to reflect that it is fallible.
For UniFFI to expose this error, your error type (E) must be an enum and implement std::error::Error (thiserror works!).

Here's how you would write a Rust failible function and how you'd expose it in UDL:

fn main() {
#[derive(Debug, thiserror::Error)]
enum ArithmeticError {
    #[error("Integer overflow on an operation with {a} and {b}")]
    IntegerOverflow { a: u64, b: u64 },

fn add(a: u64, b: u64) -> Result<u64, ArithmeticError> {
    a.checked_add(b).ok_or(ArithmeticError::IntegerOverflow { a, b })

And in UDL:

enum ArithmeticError {

namespace arithmetic {
  u64 add(u64 a, u64 b);

On the other side (Kotlin, Swift etc.), a proper exception will be thrown if Result::is_err() is true.

If you want to expose the assocated data as fields on the exception, use this syntax:

interface ArithmeticError {
  IntegerOverflow(u64 a, u64 b);