#bit-flags #flags #enums #enumflags

no-std flagset

枚举类型的数据和用于生成基于枚举的位标志的宏

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 模式

Download history 70657/week @ 2024-05-04 84342/week @ 2024-05-11 77911/week @ 2024-05-18 72651/week @ 2024-05-25 92700/week @ 2024-06-01 78836/week @ 2024-06-08 85804/week @ 2024-06-15 91180/week @ 2024-06-22 77813/week @ 2024-06-29 92281/week @ 2024-07-06 95361/week @ 2024-07-13 100654/week @ 2024-07-20 98083/week @ 2024-07-27 102595/week @ 2024-08-03 104627/week @ 2024-08-10 95642/week @ 2024-08-17

421,096 每月下载量
用于 221 个crate (20 直接)

Apache-2.0

45KB
411

Build Status Rust Version 1.65+ Crate Docs License

欢迎使用 FlagSet!

FlagSet 是一种新的、便于处理标志的方法,它结合了现有 crate(如 bitflagsenumflags)的优点,而没有它们的缺点。

现有实现

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::Serializeserde: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