Rust에는 매칭되는 pattern에 따라 코드가 실행되도록 하는 좋은 control flow construct match
가 있다. 동전을 매칭하는 경우를 생각해보자.
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
if
문을 사용한 경우에는 bool 타입을 반환해야 하지만, match
를 사용하는 경우에는 위에서 정의한 enum
타입처럼 모두 같은 타입을 갖고 있어야 한다. 각 arm
은 표현식이며, 그 결과값은 match
표현식의 전체 반환 값이 된다.
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => {
println!("Lucky penny!");
1
}
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
match arm(갈래)의 또다른 유용한 특징은 패턴에 매칭된 값을 값에 바인딩할 수 있다는 것이다. enum
변형의 하나를 바꿔 데이터를 가져 보자. 1998 ~ 2008까지 미국은 한쪽에 50개 주마다 다른 디자인의 quarter를 주조했다.
#[derive(Debug)] // so we can inspect the state in a minute
enum UsState {
Alabama,
Alaska,
// --snip--
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
친구가 모든 50개 주 quarter를 모은다고 상상해보자. 우리는 coin type 별로 정렬하고, 또한 각 quarter도 state와 결합된 이름을 call out(호출)할 것이다. 이 match 표현식에서 Coin::Quarter
와 결합할 state
변수를 추가한다. 만약 Coing::Quarter
와 match될 경우, quarter의 상태로 state
가 bind될 것이다.
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
println!("State quarter from {:?}!", state);
25
}
}
}
이전에 Option<T>
를 사용할 때 Some
케이스에서 내부 T 값을 얻고 싶었습니다. 또한 우리는 match
를 사용해 Option<T>
를 다룰 수 있따. 우리는 Option<T>
의 변형을 비교할 것이지만 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(five)
를 호출하면 변수 x
는 Some(5)
값을 갖고 함수 안으로 들어가게 되고, 각 match arm에서 비교하게 된다.
match
표현식은 가능한 모든 경우를 다뤄야 한다. 아래와 같이 plus_one
함수를 만들면 버그를 갖고 컴파일되지 않는다. 그렇기 때문에 철저하게 가능한 모든 경우를 커버해야 한다.
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
Some(i) => Some(i + 1),
}
}
$ cargo run
Compiling enums v0.1.0 (file:///projects/enums)
error[E0004]: non-exhaustive patterns: `None` not covered
enum을 사용하면서 몇가지 경우에는 special action을 나머지는 default action을 취하고 싶을 때가 있다. 주사위를 예시로 들었을 때, 아래와 같이 구현할 수 있다. 러스트에는 _
라는 특별한 패턴이 존재한다. bind 되지 않은 value와 매칭해주는 특별한 패턴이다.
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => reroll(),
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn reroll() {}
3 혹ㅇ느 7 외ㅢㅇ 값이 들어온 경우 아무 일도 일어나지 않을 때는 아래와 같이 코드를 작성할 수 있따.
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => (),
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
empty tuple에 관해서는 링크에서 더 자세한 내용을 확인할 수 있다.