5.14. 패턴 (Patterns) - 80%
패턴은 러스트에서 흔하게 나타납니다. 변수 바인딩, 정합, 그 외에서 사용합니다. 그럼 이러한 패턴들을 한번 잽싸게 살펴봅시다!
간단한 복습: 문자 match로 문자 그대로 선언하며, _
는 '어떤 경우에도'를 나타냅니다.
let x = 1;
match x {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
_ => println!("anything"),
}
one
을 출력합니다.
Multiple patterns
다수의 패턴을 |
로 묶을 수 있습니다:
let x = 1;
match x {
1 | 2 => println!("one or two"),
3 => println!("three"),
_ => println!("anything"),
}
one or two
를 출력합니다.
Destructuring
<code>구조체</code>와 같이, 사용하는 데이터 타입이 복합적이라면 패턴 내에서 디스트럭처(destructure)할 수 있습니다.
struct Point {
x: i32,
y: i32,
}
let origin = Point { x: 0, y: 0 };
match origin {
Point { x, y } => println!("({},{})", x, y),
}
:
를 써서 값에 다른 이름을 줄 수 있습니다.
struct Point {
x: i32,
y: i32,
}
let origin = Point { x: 0, y: 0 };
match origin {
Point { x: x1, y: y1 } => println!("({},{})", x1, y1),
}
만약 값들 중 몇가지에만 관심이 있다면, 다른 나머지를 써줄 필요가 없습니다:
struct Point {
x: i32,
y: i32,
}
let origin = Point { x: 0, y: 0 };
match origin {
Point { x, .. } => println!("x is {}", x),
}
x is 0
을 출력합니다.
첫번째에만 가능한 것이 아니라, 어떤 종류의 멤버도 매치 가능합니다:
struct Point {
x: i32,
y: i32,
}
let origin = Point { x: 0, y: 0 };
match origin {
Point { y, .. } => println!("y is {}", y),
}
y is 0
을 출력합니다.
'디스트럭처링(destructuring)'은 튜플이나 열거형 등, 어떤 복잡한 데이터 타입에도 작용합니다.
Ignoring bindings
You can use _
in a pattern to disregard the type and value.
For example, here’s a match
against a Result<T, E>
:
# let some_value: Result<i32, &'static str> = Err("There was an error");
match some_value {
Ok(value) => println!("got a value: {}", value),
Err(_) => println!("an error occurred"),
}
In the first arm, we bind the value inside the Ok
variant to value
. But
in the Err
arm, we use _
to disregard the specific error, and just print
a general error message.
_
is valid in any pattern that creates a binding. This can be useful to
ignore parts of a larger structure:
fn coordinate() -> (i32, i32, i32) {
// generate and return some sort of triple tuple
# (1, 2, 3)
}
let (x, _, z) = coordinate();
Here, we bind the first and last element of the tuple to x
and z
, but
ignore the middle element.
Similarly, you can use ..
in a pattern to disregard multiple values.
enum OptionalTuple {
Value(i32, i32, i32),
Missing,
}
let x = OptionalTuple::Value(5, -2, 3);
match x {
OptionalTuple::Value(..) => println!("Got a tuple!"),
OptionalTuple::Missing => println!("No such luck."),
}
This prints Got a tuple!
.
ref and ref mut
참조를 얻고 싶다면, ref
키워드를 사용합니다.
let x = 5;
match x {
ref r => println!("Got a reference to {}", r),
}
Got a reference to 5
를 출력합니다.
match
의 r
안에는 &i32
타입을 가지고 있습니다. ref
키워드는 패턴 내에서 참조를 생성한다는 뜻입니다. 만약 변경 가능한(mutable) 참조를 사용한다면, ref mut
로 써줄 수 있습니다:
let mut x = 5;
match x {
ref mut mr => println!("Got a mutable reference to {}", mr),
}
Ranges
값의 범위를 ...
로 묶을 수 있습니다:
let x = 1;
match x {
1 ... 5 => println!("one through five"),
_ => println!("anything"),
}
one through five
를 출력하게 됩니다.
범위는 정수형과 char
형에 가장 널리 쓰입니다:
let x = '💅';
match x {
'a' ... 'j' => println!("early letter"),
'k' ... 'z' => println!("late letter"),
_ => println!("something else"),
}
something else
를 출력하게 되겠죠.
Bindings
@
를 써서 변수를 이름에 바인딩할 수 있습니다:
let x = 1;
match x {
e @ 1 ... 5 => println!("got a range element {}", e),
_ => println!("anything"),
}
got a range element 1
를 출력합니다. 데이터 구조 일부분에 복잡한 작업을 하려고 할 때에 유용합니다:
#[derive(Debug)]
struct Person {
name: Option<String>,
}
let name = "Steve".to_string();
let mut x: Option<Person> = Some(Person { name: Some(name) });
match x {
Some(Person { name: ref a @ Some(_), .. }) => println!("{:?}", a),
_ => {}
}
Some("Steve")
를 출력합니다: 내부의 name
을 a
에 대입했습니다.
@
를 |
와 함께 사용한다면, 각 부분들이 모두 이름에 대입된 것을 재확인시켜주어야만 합니다.
let x = 5;
match x {
e @ 1 ... 5 | e @ 8 ... 10 => println!("got a range element {}", e),
_ => println!("anything"),
}
Guards
if
를 통해 매치 가드(match guards)를 사용할 수 있습니다:
enum OptionalInt {
Value(i32),
Missing,
}
let x = OptionalInt::Value(5);
match x {
OptionalInt::Value(i) if i > 5 => println!("Got an int bigger than five!"),
OptionalInt::Value(..) => println!("Got an int!"),
OptionalInt::Missing => println!("No such luck."),
}
Got an int!
를 출력합니다.
if
를 다수의 패턴과 사용한다면, if
는 양쪽 모두에 적용됩니다:
let x = 4;
let y = false;
match x {
4 | 5 if y => println!("yes"),
_ => println!("no"),
}
5
에만 적용되는게 아니라, 4 | 5
모두에 if
가 적용되기 때문에, no
를 출력합니다. if
가 우선순위가 되어 다음과 같이 작동하는 것입니다:
(4 | 5) if y => ...
이렇게가 아니라요:
4 | (5 if y) => ...
Mix and Match
휴! 당신이 어떻게 쓰느냐에 따라 짜 맞출수 있는(mixed and matched) 아주 많은 매치 방법들이 있습니다:
match x {
Foo { x: Some(ref name), y: None } => ...
}
패턴은 아주 강력합니다. 잘 사용해보세요.