흐름 제어 연산자

이 포스트는 Rust를 처음 공부하면서 정리한 내용입니다. 해당 포스트는 개인적으로 기억하기 위한 메모 성격에 가깝습니다. “러스트 프로그래밍 공식 가이드 (2018, 제이펍)” 서적을 참고하였으며, 러스트 공식 사이트 에서 책과 동일한 내용을 찾을 수 있습니다. 따라서 더 자세한 내용을 찾으시면 위의 링크를 참고하시면 좋습니다.

Rust는 match와 if let 이라는 강력한 흐름 제어 연산자를 제공합니다.

match

match 연산자는 패턴에 대한 풍부함도 강점이지만 컴파일러가 모든 경우의 수를 처리하고 있는제 확인할 수 있다는 장점을 가지고 있습니다. C와 비교하자면 switch 정도에 해당 되겠네요. (유연한 switch?)

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}

fn value_in_cents(coin: Coin) -> u32 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}

value_in_cents의 함수는 열거형 Coin을 인수로 받고 있습니다. 그리고 match 키워드 다음에는 비교할 표현식 을 작성합니다. 여기서 if 문과는 다른 차이점을 보이는데, match 연산자의 표현식은 모든 타입의 값을 작성할 수 있습니다. 만일 coin의 값이 Coin::Dime 이라면 10 값을 리턴합니다. match 표현식은 실행 시점에 각 가지의 패턴과 결과를 순서대로 비교합니다.

아래의 예시는 패턴이 실행될 때 실행할 코드를 가지고 있는 예제입니다.

fn value_in_cents(coin: Coin) -> u32 {
    match coin {
        Coin::Penny => {
            println!("Lucky penny!");
            1
        },
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}

값을 바인딩 하는 패턴

match 표현식의 가지 표현식이 가지는 장점은 패턴에 일치하는 값의 일부를 바인딩 할 수 있다는 점입니다. 아래의 예제는 열거자의 값을 추출하는 방법을 보여주고 있습니다.

#[derive(Debug)] // So we can inspect the state in a minute
enum UsState {
    Alabama, Alaska, ... // 생략
}

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),
}

fn value_in_cents(coin: Coin) -> u32 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter(state) => {
            println!("State quarter from {:?}!", state);
            25
        },
    }
}

앞서 작성한 Coin 와는 다르게, Coin::QuarterUsState 타입 정보를 가지고 있습니다. 새로 작성한 match 에서는 패턴에 state 변수를 추가(바인딩, Binding)해서 사용 가능합니다.

value_in_cents(Coin::Quater(UsState::Alaska));

와 같이 호출하는 경우 coin 변수는 Coin::Quarter(UsState::Alaska) 의 값을 가지게 되고, state 변수는 USSTATE::Alaska 값이 바인딩됩니다.

주의할 점

값을 match를 이용하여 바인딩 하는 경우 기본적으로 move 또는 copy 를 통해 바인딩됩니다. Reference를 바인딩 하는 경우 ref 키워드를, 가변적인 reference 변수를 사용하고 싶으면 mut ref 키워드를 사용합니다.

Option 를 이용한 매칭

이전 포스트에서 Option<T> 타입은 Null을 표현하기 위해 사용되는 표준 라이브러리이고 그 안의 Some(T)의 값을 꺼내 사용할 수 있다고 했습니다. Option 열거자의 경우에도 match 표현식을 통해 비교에 사용할 수 있습니다.

fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,
        Some(i) => Some(i + 1),
    }
}

let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);

plus_one 함수는 Option<i32> 타입을 인수로 받고 Option<i32> 타입의 값을 리턴하고 있습니다. match 표현식에서 x의 값이 None 인지, 혹은 Some 인지 검사하고 Some인 경우에는 i에 바인딩하여 Some(i + 1)의 값을 반환하고 있습니다.

이처럼 Rust에서 표현식과 열거자를 조합하는 경우는 많은 상황에서 유용하게 사용되며, Rust 코드에서도 이러한 패턴을 자주 확인할 수 있습니다.

주의할 점: match는 반드시 모든 경우를 처리해야 한다!

match 표현식에서는 반드시 모든 경우를 처리해야 합니다. 즉, 패턴 매칭은 완전해야 합니다. 만일 아래와 같이 일부의 경우를 처리하지 않는다면 컴파일조차 되지 않습니다.

fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        Some(i) => Some(i + 1),
    }
}

자리지정자 _ (The _ Placeholder)

match를 사용하면서 모든 경우를 다 처리하고 싶지 않을 경우가 있을 수 있습니다. 이때 사용 가능한 패턴도 제공합니다.

아래의 예제에서는 1, 3, 5, 7 이 아닌 0~255 범위의 수에 해당하는 패턴은 _ 자리지정자를 사용하여 대체하고 있습니다.

_ 패턴은 모든 값에 일치함을 의미합니다. 그래서 match 표현식의 가장 마지막에 추가하면 앞에 나열했던 패턴에 일치하지 않는 나머지 모든 패턴에 일치하게 됩니다. 여기서 ()는 단순한 유닛값이기 때문에 아무런 일도 일어나지 않습니다.

let some_u8_value: u8 = 0u8;
match some_u8_value {
    1 => println!("one"),
    3 => println!("three"),
    5 => println!("five"),
    7 => println!("seven"),
    _ => (),
}

if let

match 의 단점은 위의 Option의 경우와 같이 단순히 한 가지의 경우만 처리해야 할 때에는 장황하고 코드가 길어진다는 단점이 있습니다.

let some_u8_value = Some(0u8);
match some_u8_value {
    Some(3) => println!("three"),
    _ => (),
}

아래의 코드는 위의 match 예제와 동일한 동작을 수행하는 코드입니다. Some(3)의 경우만을 처리하고 그 외의 나머지의 값이나 None의 경우는 처리하지 않습니다.

if let Some(3) = some_u8_value {
    println!("three");
}

if let 문법은 하나의 패턴과 표현식을 등호 기호를 통해 조합합니다. if let 문법은 하나의 경우에만 처리하는 문법적 편의를 제공하는 장치로 생각하면 됩니다. 한편 if let 구문은 else 구문 또한 포함이 가능합니다. 이는 _ 자리지정자와 같은 역할을 합니다.

let mut count = 0;
match coin {
    Coin::Quarter(state) => println!("State quarter from {:?}!", state),
    _ => count += 1,
}
let mut count = 0;
if let Coin::Quarter(state) = coin {
    println!("State quarter from {:?}!", state);
} else {
    count += 1;
}

태그:

카테고리:

업데이트: