Option and Result
This chapter will cover the EzffiOption and EzffiResult wrappers. They're the C-types for the already-known Rust-types Option<R> and Result<T, E>, and are included in the ezffi/ezffi.h header.
ezffi ships with macros that allow the user to extract T from an Option and a Result. Don't be stupid: check if the Result is Ok and the Option is Some before unwrapping. Their definitions are:
#define EZFFI_OPTION_UNWRAP(Type, opt_ptr) ({ Type __ez_v; ezffi_option_unwrap((opt_ptr), &__ez_v); __ez_v; })
#define EZFFI_RESULT_UNWRAP(Type, res_ptr) ({ Type __ez_v; ezffi_result_unwrap((res_ptr), &__ez_v); __ez_v; })
and their usage is:
T value = EZFFI_OPTION_UNWRAP(T, opt_ptr);
T value = EZFFI_RESULT_UNWRAP(T, res_ptr);
I also provide the following functions:
// Helpers that allow the user to easily check if the wrappers
// contain any value.
bool ezffi_option_is_some(const struct EzffiOption *o);
bool ezffi_result_is_ok(const struct EzffiResult *o);
// In the case of `Result` I have this function to extract the code.
int32_t ezffi_result_error_code(const struct EzffiResult *o);
// You can also get a pointer to the error message. NULL if there
// is no error. This lives as long as EzffiResult lives.
const char *ezffi_result_error_message(const struct EzffiResult *o);
// These functions can be used to extract the generic type, but I
// recommend the macros, they're easier to read and use these
// functions internally.
//
// This functions also free the structs allocated data, so you don't need
// to call the free function after using them or the macros
void ezffi_option_unwrap(const struct EzffiOption *o, void *out);
void ezffi_result_unwrap(const struct EzffiResult *o, void *out);
// Call this function to free the allocated memory in case you didn't
// unwrap and want to ignore the returned value
void ezffi_option_free(const struct EzffiOption *o);
void ezffi_result_free(const struct EzffiResult *o);
To be able to use the EzffiResult struct, the Error type your Rust Result contains must implement From<Error> for EzffiError, From<EzffiError> for Error, and Clone. The first lets ezffi set the C-side code and message; the second lets it reconstruct an Err(Error) when C passes a Result back into Rust; Clone covers the by-ref shapes and the pre-computation of code / message at construction time. You can use as many Error types as you want but always ensure each of them has a different error code. An example would be:
#[derive(Clone)]
pub enum Error {
IOError,
SqlxError,
ReqwestError,
}
impl From<Error> for EzffiError {
fn from(e: Error) -> EzffiError {
EzffiError::new(e.code(), e.to_string())
}
}
impl From<EzffiError> for Error {
fn from(e: EzffiError) -> Error {
match e.code() {
1 => Error::IOError,
2 => Error::SqlxError,
_ => Error::ReqwestError,
}
}
}