Struct ffi_support::ExternError [−][src]
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).
-
It avoids defining a large number of
Result
-shaped types in the FFI consumer, as would be required with something like anstruct ExternResult<T> { ok: *mut T, err:... }
-
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]
fn from(e: HandleError) -> Self
[src]
impl PartialEq<ExternError> for ExternError
[src]
fn eq(&self, other: &ExternError) -> bool
[src]
fn ne(&self, other: &ExternError) -> bool
[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]
T: 'static + ?Sized,
impl<T> Borrow<T> for T where
T: ?Sized,
[src]
T: ?Sized,
impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]
T: ?Sized,
pub fn borrow_mut(&mut self) -> &mut T
[src]
impl<T> From<T> for T
[src]
impl<T, U> Into<U> for T where
U: From<T>,
[src]
U: From<T>,
impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]
U: Into<T>,
type Error = Infallible
The type returned in the event of a conversion error.
pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]
impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]
U: TryFrom<T>,