【问题标题】:Is there a way to use a boolean as an underlying type for an enum?有没有办法将布尔值用作枚举的基础类型?
【发布时间】:2019-03-03 21:14:11
【问题描述】:

我决定实现一个使用几个标志的协议,所以我开始为标志定义enums。但是,当我想定义一个具有两个值的标志时,可以是 truefalse 我收到一条错误消息:

// The protocol definition says that the flag 
// can have two values true or false, so I could just use 
// plain bool, but I want another name for true and false.
enum Flag {
    ONE = true,
    TWO = false,
}
error[E0308]: mismatched types
 --> src/lib.rs:5:11
  |
5 |     ONE = true,
  |           ^^^^ expected isize, found bool

error[E0308]: mismatched types
 --> src/lib.rs:6:11
  |
6 |     TWO = false,
  |           ^^^^^ expected isize, found bool

我想使用枚举而不是两个常量的原因是标志不是布尔值。它是表示值为真或假的标志,但我不想将普通的bools 和标志混合在一起。如果我使用bool 常量,我可以将标志值传递给每个以bool 作为参数的函数,或者在表达式中使用它们作为bool,例如

if ONE {
}

fn some_function_with_a_flag(b: bool);
// I don't want this!
some_function_with_a_flag(ONE);

在使用标志作为结构成员时,使用枚举而不是布尔常量还可以防止更多错误。以同样的方式定义了更多的标志,所以当我只使用普通的bools 和常量时,我​​会有一个类似

的结构
struct Header {
    flag1: bool,
    flag2: bool,
    flag3: bool,
}

编译器将接受切换标志值的代码:

h = Header { flag3: ONE, flag1: TWO, flag2: ONE };

当每个标志都是自己的类型(bool 的别名)时,这是不可能的。

使用值truefalse 定义枚举的意义在于协议以这种方式定义它。在我的代码中,我可能只会在打包数据以进行序列化时使用标志的布尔值(它是数据头的一部分)。

好的,编译器总是假定底层类型是isize。它可以从值中派生出来,但让我们定义它

#[repr(bool)]
enum E1 {
    ONE = true,
    TWO = false,
}
error[E0552]: unrecognized representation hint
 --> src/lib.rs:1:8
  |
1 | #[repr(bool)]
  |        ^^^^

看来我必须使用 u8 作为基础类型,然后始终将值转换为 bool

#[repr(u8)]
enum E2 {
    ONE = 1,
    TWO = 0,
}

let x = E2::ONE as bool;

这可以编译,但似乎过于复杂。有没有更好的方法来定义具有bool 值的enumbool 的类型别名是否有一个习惯用法,我可以在其中指定值?我可以这样做

enum Flag {
    TWO, 
    ONE,
}

但现在我又不得不一直将值转换为bool,并且定义的顺序看起来不自然。

由于 bool 值只会在读/写 header 时使用,所以我将把转换放入相应的函数中,并让程序的其余部分不受实现细节的影响。

【问题讨论】:

  • 既然要转换为布尔值,那么不使用布尔值的原因是什么?
  • “但现在我又不得不一直将值转换为 bool”,这就是 Rust 枚举的工作方式,它与布尔值无关:play.rust-lang.org/…
  • 你能解释一下为什么你想要一个不是 bool 的枚举,却像 bool 一样使用它吗?
  • 您无需指定#[repr(u8)] 即可将您的枚举表示为单个字节;如果判别式在界限内,无论判别式是显式的还是隐式的,编译器都已经这样做了。
  • 这两个语句似乎不一致:“我想使用枚举而不是两个常量的原因是标志不是枚举,我不想混合普通布尔值和旗帜。”和“看起来我必须使用 u8 作为基础类型,然后总是将值转换为 bool [...] 这可以编译,但似乎过于复杂。”您是否希望能够将bool 分配给Flag?如果将as bool 置入“过于复杂”,那么您如何才能将其删除而不会意外混合Flags 和bools?

标签: rust


【解决方案1】:

不,您不能使用 bool 作为枚举的基础表示。

相反,创建常量:

const ONE: bool = true;
const TWO: bool = false;

您还可以实现将枚举转换为布尔值的方法:

enum Flag {
    One,
    Two,
}

impl From<Flag> for bool {
    fn from(f: Flag) -> bool {
        match f {
            Flag::One => true,
            Flag::Two => false,
        }
    }
}

请注意,惯用的 Rust 风格使用 UpperCamelCase 表示枚举变体,使用 SHOUTING_SNAKE_CASE 表示常量。

我同意评论者的观点,即想将枚举用作布尔值很奇怪。您可以compare enums for equality 或匹配它们:

if let Flag::One = flag {
    println!("something");
} else {
    println!("something else");
}
match flag {
    Flag::One => println!("something"),
    Flag::Two => println!("something else"),
}

如果您担心大小的使用,请注意没有成员的二值枚举(最多 256 个值的枚举)与布尔值的大小相同:

enum Flag {
    One,
    Two,
}

fn main() {
    use std::mem;
    assert_eq!(mem::size_of::<Flag>(), mem::size_of::<bool>());
}

另见:

【讨论】:

  • 次要的 nitpick:您说“您可以直接比较枚举以及匹配它们”,然后提供两个匹配它们的示例,而不是比较和匹配。我会将第一个示例更改为比较。
  • @Cubic 我真的只想链接到该位的现有问答。谢谢你提醒我!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-05
相关资源
最近更新 更多