在 Rust 中,将一个枚举的成员与其他枚举类型关联并在它们的整数和字符串表示之间进行映射的惯用方法是什么?

问题描述 投票:0回答:1

目标:

  • 我想将一对 (
    usagePage
    ,
    usageID
    ) 整数转换为 Rust 枚举变体,并获取页面名称和特定用法作为字符串以进行调试。
  • 我想用最惯用的方式来定义它。
  • 我想重用
    usbd_human_interface_device::page
    模块提供的枚举。
  • 我希望此映射能够优雅地失败 - 也就是说,给定超出两个枚举范围的整数,我想检测到这一点并返回指示未找到映射的字符串。
    • 此失败案例可能是未能查找
      usagePage
      或未能在正确的枚举中查找
      usageID

具体:

挑战:

  • 枚举的类型必须在运行时确定,因为要查找的枚举取决于那时未知的整数。
    • 这意味着第二个返回值对于我想要返回的枚举集是通用的,但我不确定如何编写。我尝试定义每个枚举实现的新特征,但我无法使其工作。
  • 我想直接返回枚举类型,但我认为这是不可能的,因为枚举不是类型。 https://stackoverflow.com/a/72438660/84041似乎有类似的问题,但我不知道如何在这里应用它。
  • 我不想在此映射中过于冗长 - 我在下面有一个解决方案,但它似乎过于冗长。有更好的写法吗?

摘要:

  • 这是 Rust 中最惯用的方法吗?
// My HIDPage enum implementation
use num_enum::{TryFromPrimitive};

#[repr(u16)]
#[derive(
    Debug,
    Copy,
    Clone,
    Eq,
    PartialEq,
    Ord,
    PartialOrd,
    Hash,
    TryFromPrimitive,
)]
pub enum HIDPage {
    Desktop = 1,
    Simulation = 2,
    Game = 5,
    Keyboard = 7,
    Telephony = 0x0b,
    Consumer = 0x0c,
}
use usbd_human_interface_device::page::*;

// This enumeration, plus HIDPage, seem overly verbose
#[derive(Debug)]
pub enum HIDEnum {
    Desktop(Desktop),
    Simulation(Simulation),
    Game(Game),
    Keyboard(Keyboard),
    Telephony(Telephony),
    Consumer(Consumer),
}


fn new_usage_from_ints(page:u16 , id: u16) -> Result<String, Box<dyn Error>> {
    let knownPage: Option<HIDPage> = HIDPage::try_from(page as u16).ok();

    if let Some(knownPage) = knownPage.as_ref() {
        let knownId: Option<HIDEnum> = match knownPage {
            // Each line requires repeating the type (in different contexts) three times
            HIDPage::Desktop => Desktop::try_from(id as u8).ok().map(|v| HIDEnum::Desktop(v)),
            HIDPage::Simulation => Simulation::try_from(id as u8).ok().map(|v| HIDEnum::Simulation(v)),
            HIDPage::Game => Game::try_from(id as u8).ok().map(|v| HIDEnum::Game(v)),
            HIDPage::Keyboard => Keyboard::try_from(id as u8).ok().map(|v| HIDEnum::Keyboard(v)),
            HIDPage::Telephony => Telephony::try_from(id as u8).ok().map(|v| HIDEnum::Telephony(v)),
            HIDPage::Consumer => Consumer::try_from(id as u16).ok().map(|v| HIDEnum::Consumer(v)),
        };
        return Ok(format!("nui {:?}/{:?} (0x{:x}/0x{:x})", knownPage, knownId, page, id));
    }
    return Ok(format!("nui {:?} (0x{:x}/0x{:x})", knownPage, page, id));
}

rust types enums idioms primitive
1个回答
0
投票

更好的解决方案可能是仅拥有

HIDEnum
(下面简称为
HID
)并手动实现
TryFrom
或仅拥有其固有功能。

pub enum HID {
    Desktop(Desktop),
    Simulation(Simulation),
    Game(Game),
    Keyboard(Keyboard),
    Telephony(Telephony),
    Consumer(Consumer),
}

impl HID {
    pub fn parse(page: u16, id: u16) -> Result<HID, Box<dyn Error>> {
        let out = match page {
            1 => HID::Desktop(Desktop::try_from(u8::try_from(id)?)?),
            2 => HID::Simulation(Simulation::try_from(u8::try_from(id)?)?),
            5 => HID::Game(Game::try_from(u8::try_from(id)?)?),
            7 => HID::Keyboard(Keyboard::try_from(u8::try_from(id)?)?),
            0x0b => HID::Telephony(Telephony::try_from(u8::try_from(id)?)?),
            0x0c => HID::Consumer(Consumer::try_from(id)?),
            _ => Err(UnknownPageError)?,
        };
        Ok(out)
    }
}

但是如果您想让事情更具声明性,您可以使用单独的枚举作为页面 ID 并将其映射到

HID
:

#[repr(u16)]
#[derive(
    Debug,
    Copy,
    Clone,
    Eq,
    PartialEq,
    Ord,
    PartialOrd,
    Hash,
    TryFromPrimitive,
)]
pub enum PageId {
    Desktop = 1,
    Simulation = 2,
    Game = 5,
    Keyboard = 7,
    Telephony = 0x0b,
    Consumer = 0x0c,
}

impl HID {
    pub fn parse2(page: u16, id: u16) -> Result<HID, Box<dyn Error>> {
        let page = PageId::try_from(page)?;

        let out = match page {
            PageId::Desktop => HID::Desktop(Desktop::try_from(u8::try_from(id)?)?),
            PageId::Simulation => HID::Simulation(Simulation::try_from(u8::try_from(id)?)?),
            PageId::Game => HID::Game(Game::try_from(u8::try_from(id)?)?),
            PageId::Keyboard => HID::Keyboard(Keyboard::try_from(u8::try_from(id)?)?),
            PageId::Telephony => HID::Telephony(Telephony::try_from(u8::try_from(id)?)?),
            PageId::Consumer => HID::Consumer(Consumer::try_from(id)?),
        };
        Ok(out)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.