10 个版本
0.4.6 | 2024 年 7 月 13 日 |
---|---|
0.4.5 | 2024 年 3 月 3 日 |
0.4.4 | 2023 年 9 月 10 日 |
0.4.3 | 2022 年 2 月 9 日 |
0.3.0 | 2019 年 3 月 31 日 |
#66 in Rust 模式
421,096 每月下载量
用于 221 个crate (20 直接)
45KB
411 行
欢迎使用 FlagSet!
FlagSet 是一种新的、便于处理标志的方法,它结合了现有 crate(如 bitflags
和 enumflags
)的优点,而没有它们的缺点。
现有实现
bitflags
crate 很早就成为了 Rust 生态系统的一部分。不幸的是,它感觉不像自然的 Rust。bitflags
crate 使用奇特的 struct 格式来定义标志。标志本身只是整数常量,因此几乎没有类型安全性。但它没有任何依赖。它还允许你定义隐含的标志(也称为重叠标志)。
enumflags
crate 试图通过使用枚举来定义标志来改进 bitflags
。这对于代码的自然感觉是一个很大的改进。不幸的是,存在一些设计缺陷。为了生成标志,使用了过程宏。这意味着需要两个独立的 crate 以及额外的依赖项。此外,enumflags
使用 repr($size)
属性来指定标志的大小。不幸的是,此属性无法解析类型别名,例如 c_int
。这使得 enumflags
对于 FFI(位标志库最重要的地方)来说不是一个好的选择。《enumflags》crate 还不允许重叠标志,并且没有维护。
FlagSet 通过采用 enumflags
的自然感觉和 bitflags
的标志生成方式来改进这两者;以及额外的 API 使用便利。FlagSet 没有依赖项,并且有广泛的文档和测试。它还努力避免你犯错误,通过避免使用外部整数类型。FlagSet 还是一个零成本的抽象:所有函数都是内联的,并且应该减少到核心整数操作。FlagSet 还不依赖于 stdlib,因此它可以在 no_std
库和应用程序中使用。
定义标志
标志使用 flags!
宏定义
use flagset::{FlagSet, flags};
use std::os::raw::c_int;
flags! {
enum FlagsA: u8 {
Foo,
Bar,
Baz,
}
enum FlagsB: c_int {
Foo,
Bar,
Baz,
}
}
请注意,标志定义看起来就像一个常规枚举,但增加了字段大小类型。字段大小类型是必需的,可以是类型或类型别名。上面给出了两个示例。
另外,字段大小类型指定了相应的 FlagSet
类型的尺寸,而不是枚举本身的尺寸。要指定枚举的尺寸,请使用以下指定的 repr($size)
属性。
标志值
标志通常需要分配值。这可以通过隐式完成,其中值取决于标志的顺序。
use flagset::{FlagSet, flags};
flags! {
enum Flags: u16 {
Foo, // Implicit Value: 0b0001
Bar, // Implicit Value: 0b0010
Baz, // Implicit Value: 0b0100
}
}
或者,可以通过指定任何 const
表达式显式地定义标志值。
use flagset::{FlagSet, flags};
flags! {
enum Flags: u16 {
Foo = 0x01, // Explicit Value: 0b0001
Bar = 2, // Explicit Value: 0b0010
Baz = 0b0100, // Explicit Value: 0b0100
}
}
标志也可以重叠或“隐含”其他标志。
use flagset::{FlagSet, flags};
flags! {
enum Flags: u16 {
Foo = 0b0001,
Bar = 0b0010,
Baz = 0b0110, // Implies Bar
All = (Flags::Foo | Flags::Bar | Flags::Baz).bits(),
}
}
指定属性
属性可以用于枚举本身或其任何值。
use flagset::{FlagSet, flags};
flags! {
#[derive(PartialOrd, Ord)]
enum Flags: u8 {
Foo,
#[deprecated]
Bar,
Baz,
}
}
标志集合
标志集合是一个 FlagSet<T>
。如果您正在内存中存储标志,应使用原始的 FlagSet<T>
类型。但是,如果您想将标志作为函数的输入,应使用 impl Into<FlagSet<T>>
。这允许非常直观的 API。
use flagset::{FlagSet, flags};
flags! {
enum Flags: u8 {
Foo,
Bar,
Baz,
}
}
struct Container(FlagSet<Flags>);
impl Container {
fn new(flags: impl Into<FlagSet<Flags>>) -> Container {
Container(flags.into())
}
}
assert_eq!(Container::new(Flags::Foo | Flags::Bar).0.bits(), 0b011);
assert_eq!(Container::new(Flags::Foo).0.bits(), 0b001);
assert_eq!(Container::new(None).0.bits(), 0b000);
操作
可以在 FlagSet<F>
或单个标志上执行操作。
运算符 | 赋值运算符 | 含义 |
---|---|---|
| | |= | 并集 |
& | &= | 交集 |
^ | ^= | 切换指定的标志 |
- | -= | 差集 |
% | %= | 对称差集 |
! | 切换所有标志 |
可选 Serde 支持
Serde 支持可以通过启用 'serde' 功能标志来启用。然后您可以将 FlagSet<T>
序列化和反序列化到任何 受支持的格式。
use flagset::{FlagSet, flags};
flags! {
enum Flags: u8 {
Foo,
Bar,
}
}
let flagset = Flags::Foo | Flags::Bar;
let json = serde_json::to_string(&flagset).unwrap();
let flagset: FlagSet<Flags> = serde_json::from_str(&json).unwrap();
assert_eq!(flagset.bits(), 0b011);
对于标志枚举本身的序列化和反序列化,您可以使用 serde_repr
包(或手动实现 serde::ser::Serialize
和 serde:de::Deserialize
),结合适当的 repr
属性。
use flagset::{FlagSet, flags};
use serde_repr::{Serialize_repr, Deserialize_repr};
flags! {
#[repr(u8)]
#[derive(Deserialize_repr, Serialize_repr)]
enum Flags: u8 {
Foo,
Bar,
}
}
let json = serde_json::to_string(&Flags::Foo).unwrap();
let flag: Flags = serde_json::from_str(&json).unwrap();
assert_eq!(flag, Flags::Foo);
依赖项
~170KB