match 표현식

match

match 연산자

Rust는 각각의 값 혹은 패턴에 따라 서로 다른 분기를 이뤄 각기 다른 코드를 실행하게 하는 match 연산자를 제공하고 있다. 이 match는 (C#과 같은 프로그래밍 언어에서의) switch와 비슷한 기능을 제공한다.

match를 사용하기 위해서는, match 키워드 뒤에 분기의 소스가 되는 변수명을 적고 { ... } 괄호 안에 각 패턴별로 실행할 코드를 적으면 된다. 각 패턴별 실행코드를 지정하는 것을 갈래(arm)이라 부르는데, match 에는 보통 여러 조건들이 있으므로 하나의 match는 보통 여러 개의 갈래(arm)들을 갖는다. 예를 들어, 아래는 3개의 arm을 갖는 예를 표현한 것이다.

    match variable {
        패턴1 => 실행코드1,  // arm1
        패턴2 => 실행코드2,  // arm2
        패턴3 => 실행코드3,  // arm3
    }

match 연산자는 열거형(enum)과 함께 사용되는 경우가 많은데, 아래 예제는 함수 priority_weight()에서 Priority 열거형을 파라미터로 받아들인 후, 그 Priority값에 따라 다른 가중치(weight)값을 리턴하는 것을 표현한 것이다. 즉, High일 때는 가중치 100을 리턴하고, Medium 일 때는 가중치 10을 리턴하게 된다. 여기서의 match 패턴은 열거형의 리터럴값이었지만, 패턴에는 리터럴값, 변수명, 와일드 카드 등을 비롯하여 다양한 표현이 가능하다.

enum Priority {
    Low,
    Medium,
    High
}

fn priority_weight(priority: Priority) -> i32 {
    match priority {
        Priority::High => 100,
        Priority::Medium => 10,
        Priority::Low => {
            println!("Default priority");
            1
        }
    }
}

fn main() {
    let weight = priority_weight(Priority::High);

    println!("{}", weight); // 100
}

위의 예제에서 각 arm의 실행코드는 하나일 경우는 직접 값을 적지만, 만약 여러 문장을 실행할 경우 { } 괄호로 묶어 표현한다. 예를 들어, Priority::Low 패턴인 경우 "Default priority"를 출력하고 1을 리턴하는 2개의 문장을 { } 괄호로 묶어 주었다.

match 패턴에서 중요한 점은 가능한 패턴 전부를 모두 지정해야 한다는 것이다. 예를 들어, 위의 예에서 match 안에 Priority::Medium을 빼고, Priority::High와 Priority::Low 패턴만을 지정하면 컴파일러 에러가 발생한다.

Option 열거형 match 표현

Option<T> 열거형에 대해 match를 사용하면, 아래 예제와 같이 None과 Some 케이스에 대해 각각 다른 코드를 실행해 줄 수 있다.

fn choice(opt: Option<i32>) {
    match opt {
        None => println!("No choice"),
        Some(val) => println!("#{} was chosen", val)
    }
}

fn main() {
    let opt: Option<i32> = Option::Some(1);
    choice(opt);
}

Catch-all 패턴

match에서 일부 패턴만 처리하고 나머지 패턴들에 대해서는 하나의 실행코드를 실행할 수 있다 (즉, C/C#에서의 default 블럭처럼).

아래 예제는 변수 n의 값을 체크해서 0이면 "Zero", 1이면 "One" 을 출력하고 나머지 모두에 대해서는 숫자를 문자열로 변환해서 리턴하는 match 식을 표현하고 있다. 아래에서 Catch-all 패턴은 변수 x를 사용하고 있는데, 이는 임의의 변수명을 사용할 수 있다. Catch-all 패턴은 항상 match 의 마지막에 있어야 한다.

fn main() {
    let n: u8 = 2;

    let res = match n {
        0 => String::from("Zero"),
        1 => String::from("One"),
        x => x.to_string()
    };

    println!("{}", res);
}

Catch-all 패턴에서 만약 나머지의 경우 어떤 특별한 일을 할 필요가 없다면, 아래와 같이 패턴 변수명에 밑줄(_) 을 사용하고 실행 코드에 unit 타입 () 을 적으면 된다.

let res = match n {
    0 => String::from("Zero"),
    1 => String::from("One"),
    _ => ()
};

if let 표현

match에서 하나의 패턴에 대해 특별한 처리를 하고, 나머지를 무시하거나 혹은 나머지 전체에 대해 다른 코드 블럭을 실행하는 경우, (match 대신) "if let" 이라는 간략한 표현을 사용할 수 있다.

예를 들어, 아래와 같이 Option 매칭에서 Some인 경우만 처리하고 싶으면

fn process(opt: Option<i32>) {
    match opt {
        Some(val) => println!("#{} was chosen", val),
        _ => ()
    }
}

아래와 같이 if let을 써서 보다 간략히 표현할 수 있다.

fn process(opt: Option<i32>) {
    if let Some(val) = opt {
        println!("#{} was chosen", val);
    }
}

"if let" 뒤에서는 match에서의 "Some(val)"와 같은 패턴을 먼저 적고, 다음 match하고자 하는 변수명(여기서는 opt)를 적는다. if let의 패턴이 맞는 경우 { } 블럭 안의 코드가 실행되고, 나머지 조건의 경우(match에서의 _ 조건)는 무시된다.

만약 나머지 조건에 어떤 코드를 실행하기 위해서는 if let 블럭 뒤에 else 블럭을 사용할 수 있다.

fn process(opt: Option<i32>) {
    if let Some(val) = opt {
        println!("#{} was chosen", val);
    } else {
        println!("No option");
    }
}

This site is not affiliated with or endorsed by the Rust Foundation or Rust Project.