Expand description
Utilities for error handling in FFI bindings.
This crate facilitates an errno
-like error handling pattern: On success, the result of a
function call is returned. On error, a thread-local marker is set that allows to retrieve the
error, message, and a backtrace if available.
§Catch Errors and Panics
The catch_unwind
attribute annotates functions that can internally throw errors. It allows
the use of the questionmark operator ?
in a function that does not return Result
. The error
is then available using with_last_error
:
use relay_ffi::catch_unwind;
#[catch_unwind]
unsafe fn parse_number() -> i32 {
// use the questionmark operator for errors:
let number: i32 = "42".parse()?;
// return the value directly, not `Ok`:
number * 2
}
§Safety
Since function calls always need to return a value, this crate has to return
std::mem::zeroed()
as a placeholder in case of an error. This is unsafe for reference types
and function pointers. Because of this, functions must be marked unsafe
.
In most cases, FFI functions should return either repr(C)
structs or pointers, in which case
this is safe in principle. The author of the API is responsible for defining the contract,
however, and document the behavior of custom structures in case of an error.
§Examples
Annotate FFI functions with catch_unwind
to capture errors. The error can be inspected via
with_last_error
:
use relay_ffi::{catch_unwind, with_last_error};
#[catch_unwind]
unsafe fn parse_number() -> i32 {
"42".parse()?
}
let parsed = unsafe { parse_number() };
match with_last_error(|e| e.to_string()) {
Some(error) => println!("errored with: {error}"),
None => println!("result: {parsed}"),
}
To capture panics, register the panic hook early during library initialization:
use relay_ffi::{catch_unwind, with_last_error};
relay_ffi::set_panic_hook();
#[catch_unwind]
unsafe fn fail() {
panic!("expected panic");
}
unsafe { fail() };
if let Some(description) = with_last_error(|e| e.to_string()) {
println!("{description}");
}
§Creating C-APIs
This is an example for exposing an API to C:
use std::ffi::CString;
use std::os::raw::c_char;
#[no_mangle]
pub unsafe extern "C" fn init_ffi() {
relay_ffi::set_panic_hook();
}
#[no_mangle]
pub unsafe extern "C" fn last_strerror() -> *mut c_char {
let ptr_opt = relay_ffi::with_last_error(|err| {
CString::new(err.to_string())
.unwrap_or_default()
.into_raw()
});
ptr_opt.unwrap_or(std::ptr::null_mut())
}
Structs§
- An error representing a panic carrying the message as payload.
Functions§
- Registers a hook for capturing panics with backtraces.
- Takes the last error, leaving
None
in its place. - Acquires a reference to the last error and passes it to the callback, if any.
Attribute Macros§
- Captures errors and panics in a thread-local on
unsafe
functions.