29次发布
0.7.3 | 2024年7月29日 |
---|---|
0.7.2 | 2024年1月7日 |
0.7.1 | 2023年10月27日 |
0.6.1 | 2023年4月15日 |
0.1.1 | 2018年11月12日 |
#12 in Rust模式
3,241,100 每月下载量
用于 5,812 个crate(613个直接使用)
25KB
158 行
num_enum
过程宏,使原始类型与枚举之间的交互更简单。此crate兼容no_std。
将枚举转换为原始类型
use num_enum::IntoPrimitive;
#[derive(IntoPrimitive)]
#[repr(u8)]
enum Number {
Zero,
One,
}
fn main() {
let zero: u8 = Number::Zero.into();
assert_eq!(zero, 0u8);
}
num_enum
的 IntoPrimitive
比使用 as
更类型安全,因为 as
会静默截断 - num_enum
仅为枚举的判别类型派生 From
。
尝试使用 try_from 将原始类型转换为枚举
use num_enum::TryFromPrimitive;
use std::convert::TryFrom;
#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
#[repr(u8)]
enum Number {
Zero,
One,
}
fn main() {
let zero = Number::try_from(0u8);
assert_eq!(zero, Ok(Number::Zero));
let three = Number::try_from(3u8);
assert_eq!(
three.unwrap_err().to_string(),
"No discriminant in enum `Number` matches the value `3`",
);
}
变体选择
有时单个枚举变体可能可以用多个数值表示。
#[num_enum(alternatives = [..])]
属性允许您为单个变体定义额外的值选择。
(IntoPrimitive
的行为不受此属性的影响,它总是返回规范值。)
use num_enum::TryFromPrimitive;
use std::convert::TryFrom;
#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
#[repr(u8)]
enum Number {
Zero = 0,
#[num_enum(alternatives = [2])]
OneOrTwo = 1,
}
fn main() {
let zero = Number::try_from(0u8);
assert_eq!(zero, Ok(Number::Zero));
let one = Number::try_from(1u8);
assert_eq!(one, Ok(Number::OneOrTwo));
let two = Number::try_from(2u8);
assert_eq!(two, Ok(Number::OneOrTwo));
let three = Number::try_from(3u8);
assert_eq!(
three.unwrap_err().to_string(),
"No discriminant in enum `Number` matches the value `3`",
);
}
还支持范围表达式作为选择,但需要启用 complex-expressions
功能
use num_enum::TryFromPrimitive;
use std::convert::TryFrom;
#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
#[repr(u8)]
enum Number {
Zero = 0,
#[num_enum(alternatives = [2..16])]
Some = 1,
#[num_enum(alternatives = [17, 18..=255])]
Many = 16,
}
fn main() {
let zero = Number::try_from(0u8);
assert_eq!(zero, Ok(Number::Zero));
let some = Number::try_from(15u8);
assert_eq!(some, Ok(Number::Some));
let many = Number::try_from(255u8);
assert_eq!(many, Ok(Number::Many));
}
自定义错误类型
TryFromPrimitive
默认将使用 num_enum::TryFromPrimitiveError
作为其 Error
类型。
如果您想使用不同的类型,可以使用注解来指定
use num_enum::TryFromPrimitive;
#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
#[num_enum(error_type(name = CustomError, constructor = CustomError::new))]
#[repr(u8)]
enum FirstNumber {
Zero,
One,
Two,
}
struct CustomError {}
impl CustomError {
fn new(value: u8) -> CustomError {
CustomError {}
}
}
使用 from_primitive 安全地将原始类型转换为详尽的枚举
如果您的枚举涵盖了所有可能的原始值,您可以为此枚举派生 FromPrimitive
(这会自动实现stdlib的 From
)
您可以通过以下方式涵盖所有可能的值
- 为每个可能的值创建变体
- 拥有一个标记为
#[num_enum(default)]
- 拥有一个标记为
#[num_enum(catch_all)]
- 有
#[num_enum(alternatives = [...])
覆盖了未被变体覆盖的值。
use num_enum::FromPrimitive;
#[derive(Debug, Eq, PartialEq, FromPrimitive)]
#[repr(u8)]
enum Number {
Zero,
#[num_enum(default)]
NonZero,
}
fn main() {
assert_eq!(
Number::Zero,
Number::from(0_u8),
);
assert_eq!(
Number::NonZero,
Number::from(1_u8),
);
}
默认变体
有时在枚举中添加一个充当 wildcard 匹配尚未被其他变体覆盖的所有值的 Other
变体是有用的。
使用 #[num_enum(default)]
属性(或标准库中的 #[default]
属性)可以将变体标记为默认值。
(IntoPrimitive
的行为不受此属性的影响,它总是返回规范值。)
use num_enum::FromPrimitive;
use std::convert::TryFrom;
#[derive(Debug, Eq, PartialEq, FromPrimitive)]
#[repr(u8)]
enum Number {
Zero = 0,
#[num_enum(default)]
NonZero = 1,
}
fn main() {
let zero = Number::from(0u8);
assert_eq!(zero, Number::Zero);
let one = Number::from(1u8);
assert_eq!(one, Number::NonZero);
let two = Number::from(2u8);
assert_eq!(two, Number::NonZero);
}
只有 FromPrimitive
会关注 default
属性,TryFromPrimitive
则忽略它们。
捕获所有变体
有时需要一个 Other
变体,它将未匹配的值作为字段存储。
#[num_enum(catch_all)]
属性允许您标记最多一个变体用于此目的。应用此属性的变体必须是具有与 repr
类型完全匹配的一个字段的元组变体。
use num_enum::FromPrimitive;
use std::convert::TryFrom;
#[derive(Debug, Eq, PartialEq, FromPrimitive)]
#[repr(u8)]
enum Number {
Zero = 0,
#[num_enum(catch_all)]
NonZero(u8),
}
fn main() {
let zero = Number::from(0u8);
assert_eq!(zero, Number::Zero);
let one = Number::from(1u8);
assert_eq!(one, Number::NonZero(1_u8));
let two = Number::from(2u8);
assert_eq!(two, Number::NonZero(2_u8));
}
由于这是自然穷尽的,因此这仅支持 FromPrimitive
,不支持 TryFromPrimitive
。
使用 unchecked_transmute_from 不安全地将原始类型转换为枚举
如果您确实确定转换将成功(并且没有使用 #[num_enum(default)]
或 #[num_enum(alternatives = [..])]
中的任何变体),并且想避免少量开销,可以使用不安全的代码进行此转换。除非您有数据表明上述 try_from
中生成的匹配语句是您的瓶颈,否则您应该避免这样做,因为不安全的代码可能导致您的程序出现严重的内存问题。
use num_enum::UnsafeFromPrimitive;
#[derive(Debug, Eq, PartialEq, UnsafeFromPrimitive)]
#[repr(u8)]
enum Number {
Zero,
One,
}
fn main() {
assert_eq!(
unsafe { Number::unchecked_transmute_from(0_u8) },
Number::Zero,
);
assert_eq!(
unsafe { Number::unchecked_transmute_from(1_u8) },
Number::One,
);
}
unsafe fn undefined_behavior() {
let _ = Number::unchecked_transmute_from(2); // 2 is not a valid discriminant!
}
请注意,这个派生函数忽略了枚举上任何 default
、catch_all
和 alternatives
属性。如果您需要支持从这些值进行转换,应使用 TryFromPrimitive
或 FromPrimitive
。
这意味着,例如,以下行为是未定义的
use num_enum::UnsafeFromPrimitive;
#[derive(UnsafeFromPrimitive)]
#[repr(u8)]
enum Number {
Zero = 0,
// Same for `#[num_enum(catch_all)]`, and `#[num_enum(alternatives = [2, ...])]`
#[num_enum(default)]
One = 1,
}
let _undefined_behavior = unsafe { Number::unchecked_transmute_from(2) };
可选功能
一些枚举值可能由复杂的表达式组成,例如
enum Number {
Zero = (0, 1).0,
One = (0, 1).1,
}
为了减少编译时间,默认情况下不支持这些功能,但如果您启用了对 num_enum
的依赖中的 complex-expressions
功能,这些功能应该开始工作。
许可证
num_enum 可以根据您的选择在 BSD 3 条款、Apache 2 或 MIT 许可证下使用。
依赖项
~0.3–1MB
~23K SLoC