this post was submitted on 29 Jul 2023
11 points (92.3% liked)

Rust

7234 readers
30 users here now

Welcome to the Rust community! This is a place to discuss about the Rust programming language.

Wormhole

!performance@programming.dev

Credits

  • The icon is a modified version of the official rust logo (changing the colors to a gradient and black background)

founded 2 years ago
MODERATORS
 

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?

top 3 comments
sorted by: hot top controversial new old
[–] hansl@lemmy.world 5 points 2 years ago

I think you can implement Into 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.

[–] aloso@programming.dev 3 points 2 years ago* (last edited 2 years ago)

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 or Err variant is zero-sized, and the other variant has a niche. So, Result&lt;(), ErrorEnum> can be represented as an integer, but Result 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.

[–] Barbacamanitu@lemmy.world 1 points 2 years ago

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.