Как оператор «as» преобразует перечисления в целые числа?

Я создавал перечисление Bit для своего проекта и столкнулся с каким-то странным взаимодействием с синтаксисом as u32 (или любого другого типа). У меня есть Into<u8, u16, .. u128>, реализованный для моего перечисления Bit, поэтому я бездумно использовал Bit as u32 в своем тесте, как бы предполагая, что это синтаксический сахар для .into(). Я провел несколько тестов, которые не увенчались успехом, пытаясь найти ошибку, которую я обнаружил в том, что мое перечисление Bit было расположено следующим образом:

pub enum Bit {
    On,
    Off,
}

По прихоти я изменил его на:

pub enum Bit {
    Off,
    On,
}

Ничего об этом не думая. Я повторно запустил свои тесты, чтобы увидеть, какой тест снова не прошел, и, к моему удивлению, два дополнительных теста прошли успешно!

Я немного покопался, переключая On и Off туда и обратно и превращая as u32 в вызов .into(). И кажется, что as u32 полностью игнорирует любые реализации Into и просто конвертирует биты. В каком-то смысле это имело смысл, но как это работает, например, для u32 as f64?? Вы не можете просто преобразовать биты там. Что именно он делает?

Глядя на предложенный вопрос , кажется, что мой Enum вместо этого использует реализацию TryFrom (это тоже реализовано). Но для меня это не имеет смысла: зачем Rust использовать реализацию TryFrom, которая может вызвать панику из-за конкретно реализованной Into? Но даже это объяснение не имеет смысла, поскольку реализация TryFrom специально сопоставляет 0 с Bit::Off, 1 с Bit::On и все остальные значения с ошибками.

Обычно я просматриваю документацию по этому вопросу, но я не знаю, где конкретно найти документацию по этому вопросу, поскольку это скорее языковая функция, а не реализация чего-либо, указатель на это также помогает!

Ссылка на игровую площадку: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=c06998e915b2b4aa44d58ec67d21eabb

Для справки, вот где я использовал as u32:

fn bits_flipped(left: &BitString, right: &BitString) -> u32 {
    assert_eq!(
        left.len(),
        right.len(),
        "the length of the bitstrings is not equal. Left is {} and right is {}",
        left.len(),
        right.len()
    );

    let mut difference: u32 = 0;
    for i in 0..left.len() {
        difference += (left[i] ^ right[i]) as u32; // This line was causing issues
    }

    difference
}

Это макрос, который я использую для реализации Into:

macro_rules! bit_into_type {
    ($t:ty) => {
        impl Into<$t> for Bit {
            #![allow(clippy::from_over_into)]
            fn into(self) -> $t {
                match self {
                    Self::On => 1,
                    Self::Off => 0,
                }
            }
        }
    };
}

И, наконец, это макрос, который я использую для реализации TryFrom:

macro_rules! bit_try_from {
    ($t:ty) => {
        impl TryFrom<$t> for Bit {
            type Error = String;

            fn try_from(value: $t) -> Result<Self, Self::Error> {
                match value {
                    0 => Ok(Bit::Off),
                    1 => Ok(Bit::On),
                    value => Err(format!("Cannot represent {} as a single bit", value)),
                }
            }
        }
    };
}
as — это приведение типа , как и в других языках, например. (float) 123
vallentin 03.05.2024 01:04

Придирка: вам следует impl From<Bit> for $t вместо impl Into<$t> for Bit. Также кажется, что struct Bit(bool) сделает вашу жизнь намного проще. Преобразование в/из bool тогда является простым и в любом случае будет предпочтительнее, чем возиться с числами.

cdhowie 03.05.2024 02:15
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
2
72
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Enum::Variant as NumberType извлекает дискриминант варианта.

Если дискриминант явно не назначен, первому варианту присваивается ноль, а следующим вариантам присваиваются возрастающие дискриминанты.

as f32/f64 принимает целочисленный дискриминант и преобразует его в число с плавающей запятой.

Другие вопросы по теме