홈강좌Rust 입문·중급 47편 중 25편째
Rust match — 경우를 빠짐없이 다뤄야 컴파일된다 (E0004)
Rust 의 match 는 값이 어느 경우인지 위에서부터 맞춰 보고, 맞는 갈래를 실행한다. 열거형의 경우를 하나라도 빠뜨리면 E0004 로 컴파일이 멈춘다. 경우에 붙은 데이터는 갈래에서 이름을 붙여 꺼내고, 나머지를 한꺼번에 받을 때는 _ 를 쓴다.
01 경우마다 갈래를 쓴다
24편의 신호등마다 기다리는 초를 정해 보자.
enum Light {
Red,
Yellow,
Green,
}
fn seconds(light: Light) -> u32 {
match light {
Light::Red => 30,
Light::Yellow => 3,
Light::Green => 25,
}
}
fn main() {
println!("red: {}s", seconds(Light::Red));
println!("yellow: {}s", seconds(Light::Yellow));
println!("green: {}s", seconds(Light::Green));
} Compiling light v0.1.0 (~/projects/light)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.17s
Running `target/debug/light`
red: 30s
yellow: 3s
green: 25smatch light { … }안에경우 => 값,을 늘어놓는다. 한 줄 하나가 갈래(arm)다.- 위에서부터 차례로 맞춰 보고, 처음 맞는 갈래 하나만 실행한다.
match도 12편의if처럼 값을 낸다. 맞은 갈래의 값이match전체의 값이고, 여기서는 그것이 함수의 마지막 식이라 반환값이 됐다.
02 경우를 빠뜨리면 E0004
Yellow 갈래를 지워 보자.
enum Light {
Red,
Yellow,
Green,
}
fn seconds(light: Light) -> u32 {
match light {
Light::Red => 30,
Light::Green => 25,
}
}
fn main() {
println!("{}", seconds(Light::Red));
}error[E0004]: non-exhaustive patterns: `Light::Yellow` not covered
--> src/main.rs:8:11
|
8 | match light {
| ^^^^^ pattern `Light::Yellow` not covered
|
note: `Light` defined here
--> src/main.rs:1:6
|
1 | enum Light {
| ^^^^^
2 | Red,
3 | Yellow,
| ------ not covered
= note: the matched value is of type `Light`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
10 ~ Light::Green => 25,
11 ~ Light::Yellow => todo!(),- 무엇 — 패턴이 빠짐없지 않다(non-exhaustive).
Light::Yellow를 다루지 않았다. - 어디 — 8번째 줄의
match light.note:가 정의를 보여 주며 3번째 줄의Yellow를not covered로 짚는다. - 고치는 법 —
help:가Light::Yellow => todo!(),갈래를 더하라고 한다.~가 붙은 줄이 고친 모양이다.todo!()는 "아직 안 만들었다" 는 자리 표시로, 실제로는3처럼 알맞은 값을 넣는다.
이것이 match 의 힘이다. 나중에 Light 에 경우를 하나 더하면, 그 경우를 다루지 않은 match 를 컴파일러가 전부 찾아 준다. if 를 여러 번 늘어놓았다면 빠뜨린 곳을 사람이 찾아야 한다.
03 경우에 붙은 데이터를 꺼낸다
24편 끝의 Shape 로 넓이를 구해 보자.
enum Shape {
Circle(f64),
Rect(f64, f64),
Dot,
}
fn area(shape: Shape) -> f64 {
match shape {
Shape::Circle(r) => 3.14 * r * r,
Shape::Rect(w, h) => w * h,
Shape::Dot => 0.0,
}
}
fn main() {
println!("{}", area(Shape::Circle(2.0)));
println!("{}", area(Shape::Rect(3.0, 4.0)));
println!("{}", area(Shape::Dot));
} Compiling shape v0.1.0 (~/projects/shape)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.15s
Running `target/debug/shape`
12.56
12
0Shape::Circle(r) 갈래는 원에 붙은 반지름을 r 이라는 이름으로 꺼낸다. Shape::Rect(w, h) 는 둘을 w·h 로 꺼낸다. 8편에서 튜플을 let (a, b) = … 로 풀던 것과 같은 모양이다. 꺼낸 이름은 그 갈래 안에서만 쓴다.
04 실습 — 나머지는 _ 로
정수처럼 경우가 셀 수 없이 많으면 모두 쓸 수 없다. 이때 _ 가 "나머지 전부" 를 받는다.
fn main() {
for roll in [1, 3, 6] {
let label = match roll {
1 => "one",
6 => "six",
_ => "other",
};
println!("{roll}: {label}");
}
} Compiling dice v0.1.0 (~/projects/dice)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.16s
Running `target/debug/dice`
1: one
3: other
6: six_ 갈래는 맨 끝에 둔다. 위에서부터 맞춰 보니, 맨 앞에 두면 모든 값이 거기서 걸린다. 위 코드에서 _ => "other", 줄을 지우면 이 편의 E0004 가 다시 난다. 이번에는 무엇 줄에 어떤 숫자가 빠졌다고 나오는지 확인해 보자. 다음 편은 Rust 에서 가장 많이 쓰는 열거형, Option 이다.