NeuroWhAI의 잡블로그

[Rust] enum 사용시 주의 사항 본문

개발 및 공부/언어

[Rust] enum 사용시 주의 사항

NeuroWhAI 2018. 12. 6. 19:03


코드 출처 : https://dtolnay.github.io/rust-quiz/7


#[repr(u8)]
enum Enum {
    First,
    Second,
}

impl Enum {
    fn p(self) {
        match self {
            First => print!("1"),
            Second => print!("2"),
        }
    }
}

fn main() {
    Enum::p(unsafe {
        std::mem::transmute(1u8)
    });
}

위 코드는 UB인가? 컴파일이 되는가? 된다면 출력은?


(정답은 아래에)



































































정답은 UB가 아니고 컴파일 되며 출력은 1입니다.

설명이야 위 링크에 잘 나와있지만 정리할겸 적어봅니다.


일단 `#[repr(u8)]` 덕분에 Enum::First == 0u8, Enum::Second == 1u8이 보장된다고 합니다.

어? 그러면 출력은 Second에 해당하는 print 결과인 "2"가 나와야 하는게 아닌가? 싶지만 속임수가 있었습니다.

위 코드를 실행해보면 경고이자 힌트가 여러개 나오는데 주목할 부분이 있습니다.

warning: unreachable pattern
  --> src/main.rs:11:13
   |
10 |             First => print!("1"),
   |             ----- matches any value
11 |             Second => print!("2"),
   |             ^^^^^^ unreachable pattern
   |
   = note: #[warn(unreachable_patterns)] on by default

warning: unused variable: `First`
  --> src/main.rs:10:13
   |
10 |             First => print!("1"),
   |             ^^^^^ help: consider using `_First` instead
   |
   = note: #[warn(unused_variables)] on by default

warning: unused variable: `Second`
  --> src/main.rs:11:13
   |
11 |             Second => print!("2"),
   |             ^^^^^^ help: consider using `_Second` instead

'unreachable pattern'이라니 이상하죠? 왜 Second에 도달하지 못한다는 것일까요?

self가 First가 아니라면 Second에 매치 될 수 있는데 말이죠.

그 아래의 'unused variable'를 봅시다.

잠깐, variable이라고요?

우리가 쓴 First, Second는 enum의 variant일텐데요?

그렇습니다.

컴파일러가 저걸 변수 선언으로 인식한겁니다.

속임수를 제거하자면 아래와 같은 코드가 됩니다.

impl Enum {
    fn p(self) {
        match self {
            aaa => print!("1"),
            bbb => print!("2"),
        }
    }
}

이러니 두번째 패턴에는 도달하지 못하는 것이었습니다.

그냥 aaa라는 변수를 만들어 self를 할당하는 꼴이고 그러하니 aaa를 안썼다는 경고가 뜨는 것이죠.

무조건 매치되는 패턴입니다.

이 문제를 해결하려면 아래와 같이 해야합니다.

match self {
    Enum::First => print!("1"),
    Enum::Second => print!("2"),
}

이러면 출력은 "2"가 나옵니다.

혹은 아래 코드를 추가하면 First, Second를 올바르게 variant로 인식합니다.

use Enum::*;

표준의 몇몇 enum들(Result, Option 등)은 위 조치가 취해져 있기 때문에 그냥 Ok(x), None 이렇게 쓸 수 있는 것이었습니다.



Comments