Rust 为 u8 枚举实现 try_from

Bri*_*ian 5 rust

我有以下代码,我需要在排名之间进行直接比较。例如,我需要能够做到self as u8 + 1 == other as u8

#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
#[repr(u8)]
pub enum Rank {
    Ace = 1,
    Two,
    Three,
    Four,
    Five,
    Six,
    Seven,
    Eight,
    Nine,
    Ten,
    Jack,
    Queen,
    King,
}

impl TryFrom<u8> for Rank {
    type Error = ();

    // TODO: replace with macro or find better option
    fn try_from(v: u8) -> Result<Self, Self::Error> {
        match v {
            x if x == Rank::Ace as u8 => Ok(Rank::Ace),
            x if x == Rank::Two as u8 => Ok(Rank::Two),
            x if x == Rank::Three as u8 => Ok(Rank::Three),
            x if x == Rank::Four as u8 => Ok(Rank::Four),
            x if x == Rank::Five as u8 => Ok(Rank::Five),
            x if x == Rank::Six as u8 => Ok(Rank::Six),
            x if x == Rank::Seven as u8 => Ok(Rank::Seven),
            x if x == Rank::Eight as u8 => Ok(Rank::Eight),
            x if x == Rank::Nine as u8 => Ok(Rank::Nine),
            x if x == Rank::Ten as u8 => Ok(Rank::Ten),
            x if x == Rank::Jack as u8 => Ok(Rank::Jack),
            x if x == Rank::Queen as u8 => Ok(Rank::Queen),
            x if x == Rank::King as u8 => Ok(Rank::King),
            _ => Err(()),
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

有没有更有效的方法来编写这个而不使用宏并且基本上将其全部写出来?

cdh*_*wie 7

tl;dr:是的,有一种方法可以在不使用宏的情况下做到这一点,但它不安全。宏没问题;使用num_enum代替。


如果您愿意深入研究不安全代码的领域,您可以使用std::mem::transmute()将 转换u8Rank

fn try_from(v: u8) -> Result<Self, Self::Error> {
    match v {
        x if x >= Rank::Ace as u8 && x <= Rank::King as u8 =>
            Ok(unsafe { std::mem::transmute(x) }),
        _ => Err(()),
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,如果枚举值稍后发生更改并且x >= Rank::Ace as u8 && x <= Rank::King as u8不再保证该值是有效的枚举值,则在转换错误值时将导致未定义的行为。

如果您采用这种方法,我会在 的定义上添加非常明显的警告注释,Rank以便其他人(以及未来的您)知道在没有适当更新实现的情况下更改值try_from可能会导致 UB。

std::mem::transmute()文档中:

transmute非常不安全。有很多方法会导致此函数出现未定义的行为。transmute应该是绝对的最后手段。

这是节省 11-12 行代码的权衡,但代价是以后可能会破坏自己。我给出这个答案是为了完整起见,是为了说“是的,有一种方法可以满足您的要求,但您确实不应该这样做。

  • “_你真的不应该这样做_”特别是因为编译器能够将完整的“匹配”优化为单个比较并无论如何返回:[godbolt](https://godbolt.org/z/qhvMc6oGo) (5认同)
  • @Jmb 对的。尽管如此,我怀疑OP的目标不一定是运行时效率,而是代码的可维护性/简洁性。必须写出每个匹配臂确实感觉有点多余,所以我理解动机——这就是为什么我们有“num_enum”箱子。 (2认同)