Приведение типов

Rust не предусматривает неявного (принудительного) преобразования типов между примитивами. Однако явное преобразование типов (casting) можно выполнить, используя ключевое слово as.

Правила, используемые для преобразования внутренних типов, такие же, как в языке C, за исключением тех случаев, когда преобразование типов в языке C вызывает неопределённое поведение. Поведение всех приведений между встроенными типами чётко определено в Rust.

// Строчка ниже убирает все предупреждения,
// которые вызываются переполнением при преобразовании типов.
#![allow(overflowing_literals)]

fn main() {
    let decimal = 65.4321_f32;

    // Ошибка! Нет неявного преобразования
    let integer: u8 = decimal;
    // ИСПРАВЬТЕ ^ Закомментируйте данную строку

    // Явное преобразование
    let integer = decimal as u8;
    let character = integer as char;

    // Ошибка! Здесь ограничение в правилах конвертации.
    // Число с плавающей точкой не может быть напрямую конвертировано в символ.
    let character = decimal as char;
    // ИСПРАВЬТЕ ^ Закомментируйте данную строку

    println!("Преобразование: {} -> {} -> {}", decimal, integer, character);

    // Когда преобразовывается любое значение в беззнаковый тип T
    // std::T::MAX + 1 добавляется или вычитается до тех пор, пока значение
    // не будет помещаться в новый тип.

    // 1000 поместится в u16
    println!("1000 as u16: {}", 1000 as u16);

    // 1000 - 256 - 256 - 256 = 232
    // Подробнее. Первые 8 младших битов (LSB) сохраняются,
    // а старшие биты (MSB) будут усечены.
    println!("1000 as u8: {}", 1000 as u8);
    // -1 + 256 = 255
    println!("  -1 as u8: {}", (-1i8) as u8);

    // Для положительных чисел результатом будет остаток от деления
    println!("1000 mod 256: {}", 1000 % 256);

    // Когда значение преобразовывается в знаковый тип,
    // побитовый результат будет таким же, как и
    // первое преобразование к соответствующему типу без знака. Если старший бит этого значения
    // равен 1, то это значение — отрицательное.

    // За исключением случая, когда значение умещается в тип.
    println!(" 128 as i16: {}", 128 as i16);
    // 128 as u8 -> 128, дополнительный код которого в 8 битах:
    println!(" 128 as i8: {}", 128 as i8);

    // повторяем примеры
    // 1000 as u8 -> 232
    println!("1000 as u8: {}", 1000 as u8);
    // и дополнительный код 232 — это -24
    println!(" 232 as i8: {}", 232 as i8);
    
    // Начиная с Rust 1.45, ключевое слово `as` выполняет *насыщающее приведение* (saturating cast)
    // при преобразовании в целое число с плавающей точкой.
    // Если значение числа с плавающей точкой превышает верхнюю границу
    // или меньше нижней границы, то возвращаемое значение
    // будет равняться пересечённой границе.
    
    // 300.0 == 255
    println!("300.0 == {}", 300.0_f32 as u8);
    // -100.0 as u8 == 0
    println!("-100.0 as u8 == {}", -100.0_f32 as u8);
    // nan as u8 is 0
    println!("nan as u8 == {}", f32::NAN as u8);
    
    // Это поведение требует небольших затрат во время работы программы,
    // и его можно избежать при помощи unsafe-методов, однако результат
    // может пересечь границы, и мы получим ненадёжное значение.
    // Используйте эти методы с умом:
    unsafe {
        // 300.0 == 44
        println!("300.0 == {}", 300.0_f32.to_int_unchecked::<u8>());
        // -100.0 as u8 == 156
        println!("-100.0 as u8 == {}", (-100.0_f32).to_int_unchecked::<u8>());
        // nan as u8 == 0
        println!("nan as u8 == {}", f32::NAN.to_int_unchecked::<u8>());
    }
}