Reducing TryFrom Boilerplate Code / Validating Enum Discriminant

Currently, I have some enums that represent a group of mouse settings. I use bit shifting so I can convert all the settings to a single number that I can use to share and import settings.

Here is an example enum:

#[derive(PartialEq, Copy, Clone, Debug)]
#[repr(u32)]
pub enum DpiOptions {
    Dpi400 = 1 << 5,
    Dpi800 = 1 << 6,
    Dpi1600 = 1 << 7,
    Dpi3200 = 1 << 8,
    Dpi6400 = 1 << 9,
}

View full code here: types.rs

Now, I have a simple function that lets me convert all the current mouse settings to a single number. This works as intended. This can be seen in the linked code above. However, I needed a way to convert back from a number to a mouse settings struct with validation.

I came up with this solution, but I was wondering if there was a better way:

impl TryFrom<u32> for DpiOptions {
    type Error = &'static str;

    fn try_from(value: u32) -> Result<Self, Self::Error> {
        match value {
            x if x == DpiOptions::Dpi400 as u32 => Ok(DpiOptions::Dpi400),
            x if x == DpiOptions::Dpi800 as u32 => Ok(DpiOptions::Dpi800),
            x if x == DpiOptions::Dpi1600 as u32 => Ok(DpiOptions::Dpi1600),
            x if x == DpiOptions::Dpi3200 as u32 => Ok(DpiOptions::Dpi3200),
            x if x == DpiOptions::Dpi6400 as u32 => Ok(DpiOptions::Dpi6400),
            _ => Err("Invalid DPI"),
        }
    }
}

There are examples of me using this in my main function (where I am testing stuff).

I am very new to rust (second time using it) and I was wondering if there was a better way to check if a number matches with an enum discriminant. I thought I could use matches!, but that did not seem to work. I also do not want to use loops, which might be the only other way to simplify it. I did some research, and I guess there is a way to use macros to reduce the boilerplate code, but my brain is currently too small for that.

On a side note, if you click that link above, I make my main function async because that is what the tutorial I was semi-following used. He was using typescript, so it might not make sense for me to do the same in rust. I know tokio threads are more efficient, but I don't think I even need threads in this project. If anyone wants to look at that and just confirm with me that it does not make sense, that would be very helpful.

You're doing it correctly. This is such a common thing to do that there are derive macros for it:

1 Like

You don't need an async main (or any other async) here because the USB crate you're using (rusb) doesn't have an async API. So the USB commands are synchronous/blocking.

You can tell whether an API is async by the presence of the async keyword on the functions, and the need to do an await to call those functions.

That crate could perhaps have been implemented as async, but instead it is just a wrapper around the synchronous libusb library.

1 Like

Thanks for the quick response. This is exactly what I was looking for.

And thanks for the clarification on the async stuff. Your explanation really helped me understand when to make my main async or not.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.