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:
#[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:
[Error]
enum ArithmeticError {
"IntegerOverflow",
};
namespace arithmetic {
[Throws=ArithmeticError]
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 associated data as fields on the exception, use this syntax:
[Error]
interface ArithmeticError {
IntegerOverflow(u64 a, u64 b);
};
Interfaces as errors
It's possible to use an interface
(ie, a rust struct impl or a dyn Trait
) as an error;
the thrown object will have methods instead of fields.
This can be particularly useful when working with anyhow
style errors, where
an enum can't easily represent certain errors.
In your UDL:
namespace error {
[Throws=MyError]
void bail(string message);
}
[Traits=(Debug)]
interface MyError {
string message();
};
#[derive(Debug, thiserror::Error)]
#[error("{e:?}")] // default message is from anyhow.
pub struct MyError {
e: anyhow::Error,
}
impl MyError {
fn message(&self) -> String { self.to_string() }
}
impl From<anyhow::Error> for MyError {
fn from(e: anyhow::Error) -> Self {
Self { e }
}
}
anyhow
directly in your exposed functions - you need a wrapper:
fn oops() -> Result<(), MyError> {
let e = anyhow::Error::msg("oops");
Err(e.into())
}
try:
oops()
except MyError as e:
print("oops", e.message())
This works for procmacros too - just derive or export the types.
#[derive(Debug, uniffi::Error)]
pub struct MyError { ... }
#[uniffi::export]
impl MyError { ... }
#[uniffi::export]
fn oops(e: String) -> Result<(), Arc<MyError>> { ... }