반증 가능성(Refutability): 패턴이 매칭에 실패할지의 여부

패턴은 2가지 형태가 존재합니다. 반증 가능 패턴과 반증 불가 패턴. 주어진 어떠한 값에도 대응되는 패턴을 반증 불가(irrefutable) 패턴이라 합니다. 예를 들면 let x = 5;x가 있습니다. x에 어떠한 값이 오건 대응하기 때문에 실패할 수 없고, 곧 반증 불가합니다. 주어진 값에 대응이 실패할 수 있는 패턴을 반증 가능(refutable) 패턴이라 합니다. if let Some(x) = a_value;Some(x)가 그 예시입니다. a_value의 값이 None인 경우가 있다면 Some(x)에 대응하지 못하고 실패하게 됩니다. 즉 반증 가능합니다.

함수의 매개변수, let구문, for루프들은 반증 불가한 패턴만 허용합니다. 이 표현들의 경우 값을 패턴에 대응하는데 실패할 경우 프로그램이 할 수 있는 행동이 없기 때문입니다. 반대로 if letwhile let표현은 반증 가능 패턴만 허용합니다. 이 표현들은 성공 여부에 따라 다른 행동을 하도록 설계 됐기 때문에 실패의 여지가 있는 패턴이 올 것을 가정합니다.

일반적으로 반증 가능 패턴과 반증 불가 패턴의 차이에 대해 걱정 할 필요는 없습니다. 다만 관련된 에러메세지를 보고 코드를 고치기 위해선 이 반증 가능성이라는 개념을 숙지 해야할 필요가 있습니다. 만일 관련된 에러가 생길 경우 원래 의도한 기능에 맞춰 패턴을 고치거나, 패턴을 이용하는 구문을 고치셔야 합니다.

반증 불가한 패턴이 필요한 곳에서 반증 가능 패턴을 쓰는 경우와 그 반대의 경우를 살펴 봅시다. 예제 18-8은 let구문에서 반증 가능 패턴 Some(x)를 쓰고 있습니다. 예상 하셨듯 해당 코드는 에러가 발생합니다.

let Some(x) = some_option_value;

예제 18-8: let에서 반증 가능 패턴의 사용

some_option_valueNone일 경우 Some(x)에 대응하는데 실패합니다. 즉 반증 가능 패턴입니다. 하지만 let구문은 반증 불가한 패턴만을 허용합니다. None이 왔을 경우 할 수 있는 일이 없기 때문입니다. 러스트는 컴파일 시에 반증 불가 패턴이 필요한 곳에 반증 가능 패턴이 왔다고 불평할겁니다.

error[E0005]: refutable pattern in local binding: `None` not covered
 -->
  |
3 | let Some(x) = some_option_value;
  |     ^^^^^^^ pattern `None` not covered

Some(x)의 가능한 모든 경우를 다루지 않았기에 (정확히는 다룰 수 없었기에) 러스트는 컴파일 에러를 냅니다.

이런 문제를 해결하기 위해 패턴을 이용하는 구문을 바꾸는 방법이 있습니다. let을 쓰는 대신 if let을 쓰는 것입니다. 이 경우 패턴에 값을 대응하는데 실패하면 중괄호 안의 코드를 넘어가면 됩니다. 전 처럼 할 수 있는 일이 없지 않습니다. 예제 18-9는 18-8을 고친 코드입니다.


# #![allow(unused_variables)]
#fn main() {
# let some_option_value: Option<i32> = None;
if let Some(x) = some_option_value {
    println!("{}", x);
}
#}

예제 18-9: let 대신 if let과 반증 가능 패턴의 사용

코드에게 탈출구를 만들어줬습니다! 이 코드에 문제는 없습니다. 다만 반증 불가 패턴을 사용할 경우 에러를 받게 되겠죠. 예제 18-10 처럼 if letx처럼 모든 값에 대응되는 패턴을 쓸 경우 에러가 발생할 겁니다:

if let x = 5 {
    println!("{}", x);
};

예제 18-10: if let과 반증 불가 패턴의 사용

러스트는 틀려도 되야할 if let 구문에 틀릴리 없는 패턴을 쓰는 것은 말이 되지 않는다고 불평할겁니다:

error[E0162]: irrefutable if-let pattern
 --> <anon>:2:8
  |
2 | if let x = 5 {
  |        ^ irrefutable pattern

이 때문에 match의 갈래는 마지막 갈래를 제외하고는 반증 가능한 패턴을 써야합니다. 마지막 갈래는 빠짐 없이 대응해야 하는 match표현의 특성상 반증 불가한 패턴을 써야하고요. 러스트에서 match를 반증 불가 패턴을 가진 하나의 갈래만으로 구성하는 것은 가능합니다만 let구문 하나로 대체될 수 있기에 딱히 득 볼 것이 없습니다.

패턴이 사용 될 수 있는 코드 상의 모든 곳을 알고, 반증 가능 패턴과 반증 불가 패턴의 차이를 이해 했으니 이 다음은 패턴들을 만드는데 사용할 수 있는 모든 문법에 대해 알아봅시다.