有没有一种方法可以将布尔值用作枚举的基础类型?

时间:2018-09-27 22:03:32

标签: rust

我决定实现一个使用几个标志的协议,因此我开始为这些标志定义enum。但是,当我想定义一个具有两个值的标志时,它们可以是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

我要使用一个枚举而不是两个常量的原因是该标志不是布尔值。它是带有表示值true或false的标志,但是我不想将普通的bool和flag混合使用。如果我使用bool常量,则可以将标志值传递给每个以bool作为参数的函数,或在表达式中将其用作bool,例如

if ONE {
}

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

在将标志用作结构成员时,使用枚举而不是布尔常量也可以防止更多错误。还有更多以相同方式定义的标志,因此当我只使用普通的bool和常量时,我​​将拥有一个类似

的结构
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;

这可以编译,但是看起来过于复杂。有没有更好的方法来定义具有enum值的bool?我可以在其中指定值的bool类型别名有一个成语吗?我可以做

enum Flag {
    TWO, 
    ONE,
}

但是现在我再次必须始终将值强制转换为bool,而且定义的顺序看起来不自然。

由于bool值仅在读/写标头时使用,因此我只将转换放入相应的函数中,并使程序的其余部分不受实现细节的影响。

1 个答案:

答案 0 :(得分:5)

否,您不能使用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>());
}

另请参阅: