22个版本
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.4.2 | 2019年10月4日 |
在进程宏中排名1216
每月下载量3,014,993
用于5,740个crate(2个直接使用)
60KB
1K SLoC
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);
}
与使用 as
相比,num_enum
的 IntoPrimitive
更具有类型安全性,因为 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),
);
}
默认变体
有时希望在枚举中有一个 Other
变体,它充当一个通配符,匹配其他尚未被其他变体覆盖的值。
#[num_enum(default)]
属性(或 stdlib 的 #[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!
}
请注意,此 derive 会忽略枚举上的任何 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-clause、Apache 2 或 MIT 许可证下使用。
依赖关系
~0.3–1MB
~23K SLoC