Using ? Operator on Primitive Types


#1

I’m working with a C API that returns usize as a status code. In my Rust wrapper function, I wish to return early if the C API returns a non-zero status code. So I was wondering if I could use the ? operator for this somehow. For example something like this:

fn rust_wrapper_func() -> Result<SomeType, ErrorType> {
    c_func()?; //<--- This func returns usize and I'd like to have an early return here
    // Do something else
    //...
    //...
}

Is this possible?

I’ve tried using the Try trait, but I can’t implement it for usize due to orphan rules. Is there any way out?


#2

I am afraid that your best bet will probably be to wrap every C API call in a macro that performs an early return if the return value is nonzero.

For example, you could do this (not tested):

macro_rules! exit_if_nonzero {
    ($faillible:expr) => {
        let return_code: usize = $faillible;
        if return_code != 0 {
            // NOTE: May also use Err()? to benefit from Into conversion
            return Err(/* generate output error from return_code */);
        }
    }
}

fn rust_wrapper_func() -> Result<SomeType, ErrorType> {
    exit_if_nonzero!(c_func());
    // Do something else
    //...
    //...
}

With this approach, you are tagging every integer from the C API as the potential error that it is, while leaving the other “normal” integers from your program alone. I think this is the sanest way to handle C’s repurposing of integer return values for error-handling purpose.


#3

The typical way out is to newtype the foreign type (usize in this case) and impl the trait for the newtype. So perhaps you can define a RetCode(usize) (or similar) wrapper and then work off that (i.e. impl Try for it).


#4

Both viable ideas. I’ll see which one works better for me.

Thanks guys :slight_smile:


#5

Depending on how much unstable feature appetite you have, you can #[repr(transparent)] your wrapper struct and then declare the FFI fn as returning that (instead of the underlying type). That’ll save you from needing to do any conversions/casts in the Rust code.


#6

I would just write a function,

fn check(v: usize) -> Result<(), ErrorType> {
    if v != 0 { Err(ErrorType::Whatever) } else { Ok(()) }
}

then your code could look like:

fn rust_wrapper_func() -> Result<..., ErrorType> {
    check(c_func())?;
    ...
}

you may want to parameterize the error result if it varies from call to call.


#7

Nice. I used to to think repr(C) would achieve the same effect but turns out it doesn’t as repr(C) merely affects the struct layout, as I understand, not how values are passed to and from functions.

Thanks. I’ll consider this option as well.


#8

This reminds me a lot of something I made in my ffi_utils crate at work for checking null pointers and such.

Basically I created a Nullable trait which looks like this:

pub trait Nullable {
    const NULL: Self;

    fn is_null(&self) -> bool;
}

Then that gets used by a null_pointer_check!() macro:

macro_rules! null_pointer_check {
    ($ptr:expr) => { ... };
    ($ptr:expr, $null:expr) => { ... };
}

Which just checks if a pointer is null (the is_null() method) and bails early with Nullable::NULL. This also updates the LAST_ERROR thread local (which contains the most recent failure::Error) with NullPointer.

You could do something similar by creating some sort of CTry trait and c_try() which lets you convert an error to a C-style result.