Conversión explícita

Rust no proporciona conversión de tipo implícita (coerción) entre tipos elementales. Pero, la conversión de tipo explícita (casting) se puede realizar usando la palabra clave as.

Las reglas para convertir entre tipos integrales siguen las convenciones de C en general, excepto en los casos en que C tiene un comportamiento indefinido. El comportamiento de todas las conversiones explícitas entre tipos integrales está bien definido en Rust.

// Suprime todas las advertencias de las conversiones que se desbordan.
#![allow(overflowing_literals)]

fn main() {
    let decimal = 65.4321_f32;

    // ¡Error! Sin conversión implícita
    let entero: u8 = decimal;
    // FIXME ^ Comenta esta línea

    // Conversión explícita
    let entero = decimal as u8;
    let caracter = entero as char;

    // ¡Error! Existen limitaciones en las reglas de conversión. Un número de
    // coma flotate no se puede convertir directamente en un char.
    let caracter = decimal as char;
    // FIXME ^ Comenta esta línea

    println!("Convirtiendo: {} -> {} -> {}", decimal, entero, caracter);

    // al convertir cualquier valor a un tipo sin signo, T, T::MAX + 1 se suma
    // o resta hasta que el valor encaje en el nuevo tipo

    // 1000 ya encaja en un u16
    println!("1000 como un u16 es: {}", 1000 as u16);

    // 1000 - 256 - 256 - 256 = 232
    // Por debajo, se guardan los primeros 8 bits menos significativos (LSB),
    // mientras que el resto hacia el bit más significativo (MSB) se trunca.
    println!("1000 como un u8 es : {}", 1000 as u8);
    // -1 + 256 = 255
    println!("  -1 como un u8 es : {}", (-1i8) as u8);

    // Para números positivos, esto es lo mismo que el módulo
    println!("1000 módulo 256 es : {}", 1000 % 256);

    // Al convertir a un tipo con signo, el resultado (bit a bit) es el mismo
    // que al convertir primero al tipo sin signo correspondiente. Si el bit
    // más significativo de ese valor es 1, entonces el valor es negativo.

    // A menos que ya encaje, por supuesto.
    println!(" 128 como un i16 es: {}", 128 as i16);
    // 128 as u8 -> 128, cuyo complemento de dos en ocho bits es:
    println!(" 128 como un i8 es : {}", 128 as i8);

    // repitiendo el ejemplo anterior
    // 1000 as u8 -> 232
    println!("1000 como un u8 es : {}", 1000 as u8);
    // y el complemento de dos de 232 es -24
    println!(" 232 como un i8 es : {}", 232 as i8);
    
    // Desde Rust 1.45, la palabra clave `as` realiza una *conversión saturante*
    // cuando se convierte de coma flotante a entero.
    // Si el valor de la coma flotante excede el límite superior o es menor que
    // el límite inferior, el valor devuelto será igual al límite cruzado.
    
    // 300.0 es 255
    println!("300.0 es {}", 300.0_f32 as u8);
    // -100.0 as u8 es 0
    println!("-100.0 como u8 es {}", -100.0_f32 as u8);
    // nan as u8 es 0
    println!("nan como u8 es {}", f32::NAN as u8);
    
    // Este comportamiento incurre en un pequeño costo de tiempo de ejecución y
    // se puede evitar con métodos inseguros; sin embargo, los resultados
    // pueden desbordarse y devolver **valores incorrectos**. Utiliza estos
    // métodos con prudencia:
    unsafe {
        // 300.0 es 44
        println!("300.0 es {}", 300.0_f32.to_int_unchecked::<u8>());
        // -100.0 as u8 es 156
        println!("-100.0 como u8 es {}", (-100.0_f32).to_int_unchecked::<u8>());
        // nan as u8 es 0
        println!("nan como u8 es {}", f32::NAN.to_int_unchecked::<u8>());
    }
}