Struct ffi_support::ExternError[][src]

#[repr(C)]pub struct ExternError { /* fields omitted */ }

Represents an error that occured within rust, storing both an error code, and additional data that may be used by the caller.

Misuse of this type can cause numerous issues, so please read the entire documentation before usage.

Rationale

This library encourages a pattern of taking a &mut ExternError as the final parameter for functions exposed over the FFI. This is an “out parameter” which we use to write error/success information that occurred during the function’s execution.

To be clear, this means instances of ExternError will be created on the other side of the FFI, and passed (by mutable reference) into Rust.

While this pattern is not particularly ergonomic in Rust (although hopefully this library helps!), it offers two main benefits over something more ergonomic (which might be Result shaped).

  1. It avoids defining a large number of Result-shaped types in the FFI consumer, as would be required with something like an struct ExternResult<T> { ok: *mut T, err:... }

  2. It offers additional type safety over struct ExternResult { ok: *mut c_void, err:... }, which helps avoid memory safety errors. It also can offer better performance for returning primitives and repr(C) structs (no boxing required).

It also is less tricky to use properly than giving consumers a get_last_error() function, or similar.

Caveats

Note that the order of the fields is code (an i32) then message (a *mut c_char), getting this wrong on the other side of the FFI will cause memory corruption and crashes.

The fields are public largely for documentation purposes, but you should use ExternError::new_error or ExternError::success to create these.

Layout/fields

This struct’s field are not pub (mostly so that we can soundly implement Send, but also so that we can verify rust users are constructing them appropriately), the fields, their types, and their order are very much a part of the public API of this type. Consumers on the other side of the FFI will need to know its layout.

If this were a C struct, it would look like

struct ExternError {
    int32_t code;
    char *message; // note: nullable
};

In rust, there are two fields, in this order: code: ErrorCode, and message: *mut c_char. Note that ErrorCode is a #[repr(transparent)] wrapper around an i32, so the first property is equivalent to an i32.

The code field.

This is the error code, 0 represents success, all other values represent failure. If the code field is nonzero, there should always be a message, and if it’s zero, the message will always be null.

The message field.

This is a null-terminated C string containing some amount of additional information about the error. If the code property is nonzero, there should always be an error message. Otherwise, this should will be null.

This string (when not null) is allocated on the rust heap (using this crate’s rust_string_to_c), and must be freed on it as well. Critically, if there are multiple rust packages using being used in the same application, it must be freed on the same heap that allocated it, or you will corrupt both heaps.

Typically, this object is managed on the other side of the FFI (on the “FFI consumer”), which means you must expose a function to release the resources of message which can be done easily using the define_string_destructor! macro provided by this crate.

If, for some reason, you need to release the resources directly, you may call ExternError::release(). Note that you probably do not need to do this, and it’s intentional that this is not called automatically by implementing drop.

Example

use ffi_support::{ExternError, ErrorCode};

#[derive(Debug)]
pub enum MyError {
    IllegalFoo(String),
    InvalidBar(i64),
    // ...
}

// Putting these in a module is obviously optional, but it allows documentation, and helps
// avoid accidental reuse.
pub mod error_codes {
    // note: -1 and 0 are reserved by ffi_support
    pub const ILLEGAL_FOO: i32 = 1;
    pub const INVALID_BAR: i32 = 2;
    // ...
}

fn get_code(e: &MyError) -> ErrorCode {
    match e {
        MyError::IllegalFoo(_) => ErrorCode::new(error_codes::ILLEGAL_FOO),
        MyError::InvalidBar(_) => ErrorCode::new(error_codes::INVALID_BAR),
        // ...
    }
}

impl From<MyError> for ExternError {
    fn from(e: MyError) -> ExternError {
        ExternError::new_error(get_code(&e), format!("{:?}", e))
    }
}

Implementations

impl ExternError[src]

pub fn new_error(code: ErrorCode, message: impl Into<String>) -> Self[src]

Construct an ExternError representing failure from a code and a message.

pub fn success() -> Self[src]

Returns a ExternError representing a success. Also returned by ExternError::default()

pub fn consume_and_log_if_error(self)[src]

Helper for the case where we aren’t exposing this back over the FFI and we just want to warn if an error occurred and then release the allocated memory.

Typically, this is done if the error will still be detected and reported by other channels.

We assume we’re not inside a catch_unwind, and so we wrap inside one ourselves.

pub fn get_code(&self) -> ErrorCode[src]

Get the code property.

pub fn get_raw_message(&self) -> *const c_char[src]

Get the message property as a pointer to c_char.

pub fn get_message(&self) -> FfiStr<'_>[src]

Get the message property as an [FfiStr]

pub unsafe fn get_and_consume_message(self) -> Option<String>[src]

Get the message property as a String, or None if this is not an error result.

Safety

You should only call this if you are certain that the other side of the FFI doesn’t have a reference to this result (more specifically, to the message property) anywhere!

pub unsafe fn manually_release(self)[src]

Manually release the memory behind this string. You probably don’t want to call this.

Safety

You should only call this if you are certain that the other side of the FFI doesn’t have a reference to this result (more specifically, to the message property) anywhere!

Trait Implementations

impl Debug for ExternError[src]

impl Default for ExternError[src]

impl From<Box<dyn Any + 'static + Send, Global>> for ExternError[src]

impl From<HandleError> for ExternError[src]

impl PartialEq<ExternError> for ExternError[src]

impl RefUnwindSafe for ExternError[src]

impl Send for ExternError[src]

This is sound so long as our fields are private.

impl StructuralPartialEq for ExternError[src]

impl UnwindSafe for ExternError[src]

Auto Trait Implementations

impl !Sync for ExternError

impl Unpin for ExternError

Blanket Implementations

impl<T> Any for T where
    T: 'static + ?Sized
[src]

impl<T> Borrow<T> for T where
    T: ?Sized
[src]

impl<T> BorrowMut<T> for T where
    T: ?Sized
[src]

impl<T> From<T> for T[src]

impl<T, U> Into<U> for T where
    U: From<T>, 
[src]

impl<T, U> TryFrom<U> for T where
    U: Into<T>, 
[src]

type Error = Infallible

The type returned in the event of a conversion error.

impl<T, U> TryInto<U> for T where
    U: TryFrom<T>, 
[src]

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.