For context: I am trying to write a Rust wrapper over a C library.
Like many C libraries, most of its functions return an int
. Positive return values are meaningful (provides information) and negative values are error codes.
To give an example, think of something like int get_items_from_record(const struct record *rec, struct item *items)
. A positive value indicates how many items were returned. -1
could mean ErrorA, -2
ErrorB, and so on.
Since this is Rust, I want to represent this kind of integer as Result<T, E>, e.g.:
enum LibError {
A = -1,
B = -2,
// ....
}
// LibResult is ideally just represented as an integer.
type LibResult = Result<NonNegativeInteger, LibError>;
// Then I can pass LibResult values back to the C code as i32 trivially.
Is there a way/crate to do this?
I think you can implement Into<i32> for Result<…, LibError>. You can also implement the branching trait of Result so ? works. I’ve done it for booleans in the past as a learning experiment.
Unfortunately on mobile so I can’t link or copy paste code but that should give you some pointers to the right doc.
This is not possible, because Rust still stores a discriminant even when the enum values don’t overlap.
As far as I can tell, the only situation where Rust doesn’t store a discriminant is when either the
Ok
orErr
variant is zero-sized, and the other variant has a niche. So,Result<(), ErrorEnum>
can be represented as an integer, butResult
can not.You can still use enums, and implement simple conversions like this:
#[repr(i8)] pub enum Error { E1 = -1, E2 = -2, E3 = -3, E4 = -4, } #[repr(i8)] pub enum Success { S0 = 0, S1 = 1, S2 = 2, S3 = 3, } pub type LibResult = Result; pub fn number_to_result(value: i32) -> Option { match value { -4 ..= -1 => Some(Err(unsafe { std::mem::transmute(value as i8) })), 0 ..= 3 => Some(Err(unsafe { std::mem::transmute(value as i8) })), _ => return None, } } pub fn result_to_number(res: LibResult) -> i32 { match res { Ok(value) => value as i32, Err(error) => error as i32, } }
P.S. Sorry that the generics aren’t displayed due to Lemmy’s bad santiziation.
You’ve pretty much got it figured out. Create your result type and write an Impl for covering to/from integers. You can use From/Into to keep it idiomatic.