소개
All the Places Patterns Can Be Used에 오신 것을 환영합니다. 이 랩은 Rust Book의 일부입니다. LabEx 에서 Rust 기술을 연습할 수 있습니다.
이 랩에서는 Rust 에서 패턴을 사용할 수 있는 모든 위치를 탐구합니다.
패턴을 사용할 수 있는 모든 위치
패턴은 Rust 의 여러 곳에서 나타나며, 여러분은 깨닫지 못하는 사이에 패턴을 많이 사용해 왔습니다! 이 섹션에서는 패턴이 유효한 모든 위치에 대해 설명합니다.
match (매치) 분기
6 장에서 논의했듯이, match 표현식의 분기에서 패턴을 사용합니다. 공식적으로, match 표현식은 키워드 match, 일치시킬 값, 그리고 값과 일치하는 경우 실행할 표현식과 패턴으로 구성된 하나 이상의 match 분기로 정의됩니다. 다음과 같습니다.
match VALUE {
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
}
예를 들어, 변수 x에서 Option<i32> 값과 일치하는 Listing 6-5 의 match 표현식은 다음과 같습니다.
match x {
None => None,
Some(i) => Some(i + 1),
}
이 match 표현식의 패턴은 각 화살표 왼쪽에 있는 None과 Some(i)입니다.
match 표현식의 한 가지 요구 사항은 match 표현식의 값에 대한 모든 가능성을 고려해야 한다는 의미에서 exhaustive (전체적인) 해야 한다는 것입니다. 모든 가능성을 다루었는지 확인하는 한 가지 방법은 마지막 분기에 catchall (모든 것을 포괄하는) 패턴을 사용하는 것입니다. 예를 들어, 모든 값과 일치하는 변수 이름은 절대 실패하지 않으므로 나머지 모든 경우를 다룹니다.
특정 패턴 _는 모든 것과 일치하지만 변수에 바인딩되지 않으므로 마지막 match 분기에서 자주 사용됩니다. _ 패턴은 예를 들어 지정되지 않은 모든 값을 무시하려는 경우 유용할 수 있습니다. "패턴에서 값 무시하기"에서 _ 패턴에 대해 자세히 다룰 것입니다.
조건부 if let (if let) 표현식
6 장에서 if let 표현식을 주로 하나의 경우만 일치하는 match의 동등한 표현을 작성하는 더 짧은 방법으로 사용하는 방법에 대해 논의했습니다. 선택적으로, if let은 if let의 패턴이 일치하지 않는 경우 실행할 코드를 포함하는 해당 else를 가질 수 있습니다.
Listing 18-1 은 if let, else if, else if let 표현식을 혼합하고 일치시키는 것도 가능하다는 것을 보여줍니다. 이렇게 하면 패턴과 비교할 하나의 값만 표현할 수 있는 match 표현식보다 더 많은 유연성을 얻을 수 있습니다. 또한 Rust 는 일련의 if let, else if, else if let 분기의 조건이 서로 관련되어야 한다고 요구하지 않습니다.
Listing 18-1 의 코드는 여러 조건에 대한 일련의 검사를 기반으로 배경색을 결정합니다. 이 예제에서는 실제 프로그램이 사용자 입력에서 받을 수 있는 하드 코딩된 값을 가진 변수를 만들었습니다.
파일 이름: src/main.rs
fn main() {
let favorite_color: Option<&str> = None;
let is_tuesday = false;
let age: Result<u8, _> = "34".parse();
1 if let Some(color) = favorite_color {
2 println!(
"Using your favorite, {color}, as the background"
);
3 } else if is_tuesday {
4 println!("Tuesday is green day!");
5 } else if let Ok(age) = age {
6 if age > 30 {
7 println!("Using purple as the background color");
} else {
8 println!("Using orange as the background color");
}
9 } else {
10 println!("Using blue as the background color");
}
}
Listing 18-1: if let, else if, else if let, 및 else 혼합
사용자가 좋아하는 색상을 지정하면 [1] 해당 색상이 배경으로 사용됩니다 [2]. 좋아하는 색상이 지정되지 않았고 오늘이 화요일인 경우 [3] 배경색은 녹색입니다 [4]. 그렇지 않고 사용자가 나이를 문자열로 지정하고 이를 숫자로 성공적으로 구문 분석할 수 있는 경우 [5] 숫자의 값에 따라 색상은 보라색 [7] 또는 주황색 [8]입니다 [6]. 이러한 조건이 적용되지 않으면 [9] 배경색은 파란색입니다 [10].
이 조건부 구조를 통해 복잡한 요구 사항을 지원할 수 있습니다. 여기에 있는 하드 코딩된 값을 사용하면 이 예제는 Using purple as the background color를 출력합니다.
if let은 match 분기가 할 수 있는 것과 동일한 방식으로 그림자 변수를 도입할 수도 있습니다. if let Ok(age) = age 라인 [5]은 Ok 변형 내의 값을 포함하는 새로운 그림자 age 변수를 도입합니다. 즉, if age > 30 조건 [6]을 해당 블록 내에 배치해야 합니다. 이 두 조건을 if let Ok(age) = age && age > 30으로 결합할 수 없습니다. 30 과 비교하려는 그림자 age는 새 범위가 중괄호로 시작될 때까지 유효하지 않습니다.
if let 표현식을 사용하는 단점은 컴파일러가 exhaustiveness (전체성) 을 확인하지 않는다는 것입니다. 반면 match 표현식에서는 확인합니다. 마지막 else 블록 [9]을 생략하여 일부 경우를 처리하지 못한 경우 컴파일러는 가능한 논리 버그에 대해 경고하지 않습니다.
while let (while let) 조건 루프
if let과 유사하게 구성된 while let 조건 루프는 패턴이 계속 일치하는 동안 while 루프가 실행되도록 허용합니다. Listing 18-2 에서 우리는 벡터를 스택으로 사용하고 벡터의 값을 푸시된 반대 순서로 출력하는 while let 루프를 코딩합니다.
파일 이름: src/main.rs
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(top) = stack.pop() {
println!("{top}");
}
Listing 18-2: stack.pop()이 Some을 반환하는 동안 값을 출력하기 위해 while let 루프 사용
이 예제는 3, 2, 그리고 1을 출력합니다. pop 메서드는 벡터에서 마지막 요소를 가져와 Some(value)를 반환합니다. 벡터가 비어 있으면 pop은 None을 반환합니다. while 루프는 pop이 Some을 반환하는 동안 블록의 코드를 계속 실행합니다. pop이 None을 반환하면 루프가 중지됩니다. 우리는 while let을 사용하여 스택에서 모든 요소를 꺼낼 수 있습니다.
for 루프
for 루프에서 키워드 for 바로 뒤에 오는 값은 패턴입니다. 예를 들어, for x in y에서 x는 패턴입니다. Listing 18-3 은 for 루프에서 패턴을 사용하여 튜플을 destructure (분해) 하는 방법을 보여줍니다. 이는 for 루프의 일부입니다.
파일 이름: src/main.rs
let v = vec!['a', 'b', 'c'];
for (index, value) in v.iter().enumerate() {
println!("{value} is at index {index}");
}
Listing 18-3: 튜플을 destructure 하기 위해 for 루프에서 패턴 사용
Listing 18-3 의 코드는 다음을 출력합니다.
a is at index 0
b is at index 1
c is at index 2
enumerate 메서드를 사용하여 반복자를 조정하여 해당 값과 해당 값의 인덱스를 생성하고, 이를 튜플에 넣습니다. 생성된 첫 번째 값은 튜플 (0, 'a')입니다. 이 값이 패턴 (index, value)와 일치하면 index는 0이 되고 value는 'a'가 되어 출력의 첫 번째 줄을 인쇄합니다.
let (let) 문
이 장 이전에는 match와 if let에서만 패턴을 사용하는 것을 명시적으로 논의했지만, 사실 let 문을 포함하여 다른 곳에서도 패턴을 사용했습니다. 예를 들어, let을 사용한 이 간단한 변수 할당을 생각해 보십시오.
let x = 5;
이와 같이 let 문을 사용할 때마다 패턴을 사용한 것입니다. 더 공식적으로, let 문은 다음과 같습니다.
let PATTERN = EXPRESSION;
PATTERN 슬롯에 변수 이름이 있는 let x = 5;와 같은 문에서 변수 이름은 특히 단순한 형태의 패턴입니다. Rust 는 표현식을 패턴과 비교하고 발견된 모든 이름을 할당합니다. 따라서 let x = 5; 예제에서 x는 "여기에 일치하는 것을 변수 x에 바인딩하라"는 의미의 패턴입니다. 이름 x가 전체 패턴이므로 이 패턴은 효과적으로 "모든 값을 변수 x에 바인딩하라"는 의미입니다.
let의 패턴 매칭 측면을 더 명확하게 보려면 Listing 18-4 를 고려하십시오. 이 코드는 let과 함께 패턴을 사용하여 튜플을 destructure 합니다.
let (x, y, z) = (1, 2, 3);
Listing 18-4: 튜플을 destructure 하고 세 개의 변수를 한 번에 생성하기 위해 패턴 사용
여기서 우리는 튜플을 패턴과 일치시킵니다. Rust 는 값 (1, 2, 3)을 패턴 (x, y, z)와 비교하고 값과 패턴이 일치하는지 확인합니다. 즉, 두 요소의 수가 동일한지 확인합니다. 따라서 Rust 는 1을 x에, 2를 y에, 3을 z에 바인딩합니다. 이 튜플 패턴을 세 개의 개별 변수 패턴을 중첩하는 것으로 생각할 수 있습니다.
패턴의 요소 수가 튜플의 요소 수와 일치하지 않으면 전체 유형이 일치하지 않아 컴파일러 오류가 발생합니다. 예를 들어, Listing 18-5 는 세 개의 요소가 있는 튜플을 두 개의 변수로 destructure 하려는 시도를 보여주며, 이는 작동하지 않습니다.
let (x, y) = (1, 2, 3);
Listing 18-5: 튜플의 요소 수와 일치하지 않는 변수를 가진 패턴을 잘못 구성
이 코드를 컴파일하려고 하면 다음과 같은 유형 오류가 발생합니다.
error[E0308]: mismatched types
--> src/main.rs:2:9
|
2 | let (x, y) = (1, 2, 3);
| ^^^^^^ --------- this expression has type `({integer}, {integer},
{integer})`
| |
| expected a tuple with 3 elements, found one with 2 elements
|
= note: expected tuple `({integer}, {integer}, {integer})`
found tuple `(_, _)`
오류를 수정하려면 "패턴에서 값 무시하기"에서 보듯이 _ 또는 ..를 사용하여 튜플의 하나 이상의 값을 무시할 수 있습니다. 문제가 패턴에 변수가 너무 많은 경우, 해결책은 변수 수를 튜플의 요소 수와 같게 하여 유형이 일치하도록 변수를 제거하는 것입니다.
함수 매개변수
함수 매개변수도 패턴이 될 수 있습니다. Listing 18-6 의 코드는 i32 타입의 x라는 매개변수 하나를 받는 foo라는 함수를 선언하며, 이제 익숙해 보일 것입니다.
fn foo(x: i32) {
// code goes here
}
Listing 18-6: 매개변수에서 패턴을 사용하는 함수 시그니처
x 부분은 패턴입니다! let과 마찬가지로, 함수의 인수에 있는 튜플을 패턴과 일치시킬 수 있습니다. Listing 18-7 은 튜플을 함수에 전달할 때 튜플의 값을 분할합니다.
파일 이름: src/main.rs
fn print_coordinates(&(x, y): &(i32, i32)) {
println!("Current location: ({x}, {y})");
}
fn main() {
let point = (3, 5);
print_coordinates(&point);
}
Listing 18-7: 튜플을 destructure 하는 매개변수가 있는 함수
이 코드는 Current location: (3, 5)를 출력합니다. 값 &(3, 5)는 패턴 &(x, y)와 일치하므로 x는 값 3이고 y는 값 5입니다.
13 장에서 논의했듯이 클로저는 함수와 유사하므로 함수 매개변수 목록과 동일한 방식으로 클로저 매개변수 목록에서도 패턴을 사용할 수 있습니다.
지금까지 패턴을 사용하는 몇 가지 방법을 살펴보았지만, 패턴은 사용할 수 있는 모든 곳에서 동일하게 작동하지 않습니다. 어떤 곳에서는 패턴이 irrefutable(부정할 수 없는) 해야 하고, 다른 상황에서는 refutable(부정할 수 있는) 할 수 있습니다. 다음에서 이 두 가지 개념에 대해 논의하겠습니다.
요약
축하합니다! 패턴을 사용할 수 있는 모든 위치 (All the Places Patterns Can Be Used) 랩을 완료했습니다. LabEx 에서 더 많은 랩을 연습하여 기술을 향상시킬 수 있습니다.