Module ffi_helpers::error_handling [] [src]

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

#[macro_use]
extern crate ffi_helpers;
extern crate failure;
extern crate libc;

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_last_error

Clear the LAST_ERROR.

error_message

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

error_message_utf8

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

error_message_utf16

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

last_error_length

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

last_error_length_utf16

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

take_last_error

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

update_last_error

Update the thread_local error, taking ownership of the Error.