100 Exercises To Learn Rust - 2.10 casting 풀기

// TODO: 이 섹션에서 배운 내용을 바탕으로 `todo!()`를 변환 후의 올바른 값을 사용해 수정하세요.

#[cfg(test)]
mod tests {

    #[test]
    fn u16_to_u32() {
        let v: u32 = todo!();
        assert_eq!(47u16 as u32, v);
    }

    #[test]
    fn u8_to_i8() {
        // The compiler is smart enough to know that the value 255 cannot fit
        // inside an i8, so it'll emit a hard error. We intentionally disable
        // this guardrail to make this (bad) conversion possible.
        // The compiler is only able to pick on this because the value is a
        // literal. If we were to use a variable, the compiler wouldn't be able to
        // catch this at compile time.
        #[allow(overflowing_literals)]
        let x = { 255 as i8 };

        // You could solve this by using exactly the same expression as above,
        // but that would defeat the purpose of the exercise. Instead, use a genuine
        // `i8` value that is equivalent to `255` when converted to `u8`.
        let y: i8 = todo!();

        assert_eq!(x, y);
    }

    #[test]
    fn bool_to_u8() {
        let v: u8 = todo!();
        assert_eq!(true as u8, v);
    }
}

 

overflow-checks를 설정하거나, wrapping, saturating 메서드를 사용하는 방법을 알아보았는데, 아무리 그래도 타입을 명시적으로 변환하는 방법 하나쯤은 있을 것이다.

 

러스트에서는 as를 이용하여 명시적 형변환을 수행할 수 있다.

다른 언어에서와 마찬가지로, 명시적 형변환을 할 때 더 작은 타입으로 변환하게 되면 값이 버려질 수 있으니 주의해야 한다. as를 이용한 형변환은 절대로 잘못될 수 없는(infallible) 변환이기 때문이다.

 

여기서 infallible은 사용자가 명시한 형변환이므로, 컴파일러는 값이 버려지더라도 오류를 잡지 않는다는 의미로 보면 될 것 같다.

 

해답:

#[cfg(test)]
mod tests {

    #[test]
    fn u16_to_u32() {
        let v: u32 = 47;
        assert_eq!(47u16 as u32, v);
    }

    #[test]
    fn u8_to_i8() {
        //컴파일러는 255 값이 i8에 들어갈 수 없다는 것을 충분히 똑똑하게 알고 있기 때문에 하드 에러를 발생시킵니다.
        // 이 (잘못된) 변환을 가능하게 하기 위해 의도적으로 이 가드레일을 비활성화했습니다.
        // 컴파일러는 값이 리터럴이기 때문에 이를 선택할 수만 있습니다.
        // 만약 변수를 사용한다면 컴파일러는 컴파일 타임에 이를 포착할 수 없습니다.
        #[allow(overflowing_literals)]
        let x = { 255 as i8 };

        //위와 똑같은 식을 사용하여 이 문제를 해결할 수도 있지만, 그렇게 하면 연습의 목적에 어긋납니다.
        // 대신 `u8`로 변환하면 `255`에 해당하는 진짜 `i8` 값을 사용하십시오.
        let y: i8 = -1;

        assert_eq!(x, y);
    }

    #[test]
    fn bool_to_u8() {
        let v: u8 = 1;
        assert_eq!(true as u8, v);
    }
}​

u16_to_u32:

u16타입의 47u32로 변환한 값과, u32 타입의 v를 비교하고 있다.

47이라는 숫자는 u16에도, u32에도 충분히 들어갈 수 있는 작은 값이므로 어렵게 생각할 필요 없이 v47이면 된다.

u8_to_i8:

x255라는 값을 의도적으로 i8 타입에 넣은 값이다.

i8은 -128 ~ 127의 값을 가진다. 2.8에서 wrapping을 하도록 설정했으므로, 이를 고려해야 한다.

 

128이라는 값을 i8 타입에 저장하면 어떻게 될까? 127 + 1인데, 최댓값이 127이므로 wrapping되어 -128로 저장될 것이다.

255는 127 + 128이다. wrapping되면 127 + 1 + 127, 즉 -128에서 127을 더한 -1로 저장될 것이다.

 

따라서 y의 값이 -1이어야 테스트를 통과할 수 있다.

bool_to_u8:

러스트에서는 기본적으로 truefalse라는 불리언 타입을 지원하므로 억지로 변환할 필요는 없다.

다만 false는 0, true는 0 이외의 숫자(대부분 1)에 대응할 것이라는 느낌이 온다. C언어가 그렇기 때문에.

 

실제로 rust 레퍼런스를 찾아보면 "Primitive to Interger Cast" 부분에서 "False casts to 0, true casts to 1"이라고 명시되어 있다.

 

따라서 true as u8의 값은 1이 될 것이므로, v의 값을 1로 하면 테스트를 통과할 수 있다.