형변환

러스트는 기본 타입 간 암묵적 형변환 기능을 제공하지 않습니다. 형변환은 as 키워드를 사용해 명시적으로 수행해야 합니다.

정수 타입 간의 형변환 규칙은 일반적으로 C언어 규칙을 따릅니다. (C언어에서 동작하지만 정의되지 않은 동작(UB)인 경우는 제외됩니다.) 정수 타입 간 형변환은 모두 러스트에 잘 정의되어 있습니다.

// 오버플로우 형변환 경고를 모두 숨깁니다.
#![allow(overflowing_literals)]

fn main() {
    let decimal = 65.4321_f32;

    // 에러! 암묵적 형변환은 불가능합니다
    let integer: u8 = decimal;
    // 고쳐주세요! ^ 이 줄을 주석 처리해주세요

    // 명시적 형변환
    let integer = decimal as u8;
    let character = integer as char;

    // 에러! 변환 규칙에는 제한이 존재합니다.
    // 부동 소수점 타입은 char 타입으로 직접 변환할 수 없습니다.
    let character = decimal as char;
    // 고쳐주세요! ^ 이 줄을 주석 처리해주세요

    println!("형변환: {} -> {} -> {}", decimal, integer, character);

    // 어떤 값을 부호 없는 타입 T로 형변환 할 경우,
    // 값을 새로운 타입에 저장 할 수 있을때까지
    // 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 % 256  = {}", 1000 % 256);

    // 부호 있는 타입으로 형변환 할 경우,
    // (비트 단위) 결과는 부호 없는 타입으로 형변환한 것과 같습니다.
    // 만약 값의 최상위 비트(MSB)가 1일 경우, 해당 값은 음수입니다.

    // 물론, 바로 저장 가능한 경우는 제외고요.
    println!(" 128 as i16 = {}", 128 as i16);
    // 128 as u8 -> 128, 8비트 상에서 2의 보수:
    println!(" 128 as i8  = {}", 128 as i8);

    // 앞선 예시 반복
    // 1000 as u8 -> 232
    println!("1000 as u8  = {}", 1000 as u8);
    // 232에 대한 2의 보수는 -24입니다
    println!(" 232 as i8  = {}", 232 as i8);
    
    // 러스트 1.45 버전부터, `as` 키워드는 부동 소수점을 정수로 형변환 할 경우
    // *포화 연산(saturate 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 = 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>());
    }
}