Подтвердить что ты не робот

Как конвертировать между цифровыми типами безопасно и идиоматически?

Примечание редактора: этот вопрос относится к версии Rust до 1.0 и содержит ссылки на некоторые элементы, которых нет в Rust 1.0. Ответы по-прежнему содержат ценную информацию.

Какой идиоматический способ конвертировать из (скажем) usize в u32?

Например, приведение с использованием 4294967295us as u32 работает и справочные документы Rust 0.12 по приведению типов говорят

Числовое значение может быть приведено к любому числовому типу. Необработанное значение указателя может быть приведено к или из любого целочисленного типа или необработанного указателя. Любое другое приведение не поддерживается и не будет компилироваться.

но 4294967296us as u32 будет тихо переполнен и даст результат 0.

Я нашел ToPrimitive и FromPrimitive которые предоставляют хорошие функции, такие как to_u32() → Option<u32>, но они помечены как нестабильные:

#[unstable(feature = "core", reason = "trait is likely to be removed")]

Какой идиоматический (и безопасный) способ преобразования между числовыми (и указательными) типами?

Размер isize/usize зависящий от isize является одной из причин, по которой я usize этот вопрос - исходный сценарий состоял в том, что я хотел преобразовать из u32 в usize чтобы я мог представить дерево в Vec<u32> (например, let t = Vec![0u32, 0u32, 1u32], тогда получить прародителя узла 2 было бы t[t[2us] as usize]), и я удивлялся, как это не usize если usize будет меньше 32 бит.

4b9b3361

Ответ 1

On ToPrimitive/FromPrimitive

RFC 369, Num Reform, заявляет:

В идеале все [...] ToPrimitive [...] должны быть удалены в пользу более принципиального способа работы с C-подобными перечислениями

В то же время, эти черты живут в ящике:

Работа без черт

От типа, который полностью вписывается в другой

Здесь нет проблем. Используйте From чтобы указать, что потери не происходят:

fn example(v: i8) -> i32 {
    i32::from(v) // or v.into()
}

Вы можете использовать as, но рекомендуется избегать этого, когда вам это не нужно (см. Ниже):

fn example(v: i8) -> i32 {
    v as i32
}

От типа, который не вписывается полностью в другой

Нет единого метода, который имеет общий смысл - вы спрашиваете, как разместить две вещи в пространстве, предназначенном для одной. Одна хорошая первоначальная попытка состоит в том, чтобы использовать Option - Some когда значение подходит, и None противном случае. Затем вы можете потерпеть неудачу в вашей программе или заменить значение по умолчанию, в зависимости от ваших потребностей.

Начиная с Rust 1.34, вы можете использовать TryFrom:

use std::convert::TryFrom;

fn example(v: i32) -> Option<i8> {
    i8::try_from(v).ok()
}

Перед этим вам придется написать подобный код самостоятельно:

fn example(v: i32) -> Option<i8> {
    if v > std::i8::MAX as i32 {
        None
    } else {
        Some(v as i8)
    }
}

Что as делает

но 4294967296us as u32 будет тихо переполнен и даст результат 0

При преобразовании в меньший тип, так as просто принимает младшие биты числа, игнорируя старшие биты, включая знак:

fn main() {
    let a: u16 = 0x1234;
    let b: u8 = a as u8;
    println!("0x{:04x}, 0x{:02x}", a, b); // 0x1234, 0x34

    let a: i16 = -257;
    let b: u8 = a as u8;
    println!("0x{:02x}, 0x{:02x}", a, b); // 0xfeff, 0xff
}