Module ffi_helpers::error_handling[][src]

Expand description

Common error handling routines.

The main error handling method employed is a thread-local variable called LAST_ERROR which holds the most recent error as well as some convenience functions for getting/clearing this variable.

The theory is if a function fails then it should return an “obviously invalid” value (typically -1 or 0 when returning integers or NULL for pointers, see the Nullable trait for more). The user can then check for this and consult the most recent error for more information. Of course that means all fallible operations must update the most recent error if they fail and that you must check the returned value of any fallible operation.

While it isn’t as elegant as Rust’s monad-style Result<T, E> with ? and the various combinators, it actually turns out to be a pretty robust error handling technique in practice.

Note: It is highly recommended to have a skim through libgit2’s error handling docs. The error handling mechanism used here takes a lot of inspiration from libgit2.

Examples

The following shows a full example where our write_data() function will try to write some data into a buffer. The first time through

use libc::{c_char, c_int};
use std::slice;
use ffi_helpers::error_handling;

fn main() {
    if unsafe { some_fallible_operation() } != 1 {
        // Before we can retrieve the message we need to know how long it is.
        let err_msg_length = error_handling::last_error_length();

        // then allocate a big enough buffer
        let mut buffer = vec![0; err_msg_length as usize];
        let bytes_written = unsafe {
            let buf = buffer.as_mut_ptr() as *mut c_char;
            let len = buffer.len() as c_int;
            error_handling::error_message_utf8(buf, len)
        };

        // then interpret the message
        match bytes_written {
            -1 => panic!("Our buffer wasn't big enough!"),
            0 => panic!("There wasn't an error message... Huh?"),
            len if len > 0 => {
                buffer.truncate(len as usize - 1);
                let msg = String::from_utf8(buffer).unwrap();
                println!("Error: {}", msg);
            }
            _ => unreachable!(),
        }
    }
}

/// pretend to do some complicated operation, returning whether the
/// operation was successful.
#[no_mangle]
unsafe extern "C" fn some_fallible_operation() -> c_int {
    match do_stuff() {
        Ok(_) => 1, // do_stuff() always errors, so this is unreachable
        Err(e) => {
            ffi_helpers::update_last_error(e);
            0
        }
    }
}

Functions

Clear the LAST_ERROR.

Peek at the most recent error and get its error message as a Rust String.

Peek at the most recent error and write its error message (Display impl) into the provided buffer as a UTF-8 encoded string.

Peek at the most recent error and write its error message (Display impl) into the provided buffer as a UTF-16 encoded string.

Get the length of the last error message in bytes when encoded as UTF-8, including the trailing null.

Get the length of the last error message in bytes when encoded as UTF-16, including the trailing null.

Take the most recent error, clearing LAST_ERROR in the process.

Update the thread_local error, taking ownership of the Error.