Есть ли способ использовать логическое значение в качестве базового типа для перечисления? - PullRequest
0 голосов
/ 28 сентября 2018

Я решил реализовать протокол, который использует пару флагов, поэтому я начал определять enum s для флагов.Однако, когда я хочу определить флаг, который имеет два значения, которые могут быть true или false, я получаю сообщение об ошибке:

// 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

Причина, по которой я хочу использовать перечисление вместоиз двух констант является то, что флаг не является bool.Это флаг со значением представления true или false, но я не хочу смешивать обычные bool s и 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);

Используя вместо этого enumконстант bool также предотвращает появление некоторых ошибок при использовании флага в качестве члена структуры.Есть больше флагов, определенных таким же образом, поэтому, когда я просто использую простые bool s и константы, у меня будет структура, подобная

struct Header {
    flag1: bool,
    flag2: bool,
    flag3: bool,
}

Компилятор примет код, где переключаются значения флага:

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

Это невозможно, когда каждый флаг имеет свой собственный тип (псевдоним для bool).

Точка определения перечисления со значениями true и false просточто протокол определяет это таким образом.В моем коде я, вероятно, буду использовать логическое значение флагов, только когда данные упакованы для сериализации (это часть заголовка данных).

Хорошо, компилятор всегда предполагает, что базовый тип равен 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 голосов
/ 28 сентября 2018

Нет, вы не можете использовать 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 для констант.

Я согласен с комментаторами, что странно хотеть использовать enum в качестве логического значения.Вы можете сравнить перечисления на равенство или сопоставить их:

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>());
}

См. Также:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...