[Rust] 패턴
Updated:
개요
- 단순하거나 복잡한 타입의 구조에 값들을 비교하기 위한 문법
- match 표현 및 다른 구문들과 함께 사용하면 더 많은 흐름 제어 가능
- 패턴은 다음의 조합으로 이루어짐
- 리터럴 값(Literals)
- 분해한 배열(Array), 열거형(Enum), 구조체(Struct), 튜플(Tuple)
- 변수(Variable)
- 와일드카드(Wildcard)
- 임시 값(Placeholders)
- match
- 값이 가질 수 있는 모든 경우의 수를 표현해야 함
_
패턴을 통해 명시하지 않는 값들 무시 가능
if let
- 주로 갈래가 하나 밖에 없는 match를 표현할 때 사용
- 새로운 변수는 스코프가 시작하기 전까지 유효하지 않음
if let Some(_i) = option && _i == 1 {
불가
else if let
도 가능- match와 다르게 컴파일러가 해당 구문이 모든 경우를 다뤘는지 판단하지 않음
while let
- 주어진 값이 패턴에 계속 대응되는한 실행
for
- for 키워드 바로 다음에 오는 값이 패턴(
for x in y
에서 x가 패턴)
- for 키워드 바로 다음에 오는 값이 패턴(
let
let PATTERN = EXPRESSION;
- 함수의 매개변수
let
과 같음
- 클로져의 매개변수
- 함수와 같음
- 반증 가능성(refutability)
- 패턴이 매칭에 실패할지의 여부
- 반증 불가(irrefutable) 패턴
- 주어진 어떠한 값에도 대응되는 패턴
let x = 1;
의x
- 어떠한 값이 오더라도 x에 대응하므로 실패할 수 없으므로 반증할 수 없음
- 반증 가능(refutable) 패턴
- 주어진 값에 대응이 실패할 수 있는 패턴
if let Some(x) = value;
의Some(x)
- value가 None이면 Some(x)에 대응하지 못하고 실패하므로 반증 가능
- 함수의 매개변수, let, for는 반증 불가 패턴만 허용
- 패턴에 값을 대응하는데 실패할 경우 프로그램이 할 수 있는 행동이 없기 때문
if let
과while let
은 반증 가능 패턴만 허용- 성공 여부에 따라 다른 행동을 하도록 설계가 되었으므로 실패의 여지가 있는 패턴이 올 것을 가정
- 패턴 구문
- 리터럴 매칭
- 명명 변수 매칭
- 명명 변수는 어떠한 값에도 매칭되는 반증 불가능한 패턴
- 다중 패턴
- match 표현 내에서 or 을 뜻하는
|
구문을 이용해 여러개의 패턴과 매치 가능
- match 표현 내에서 or 을 뜻하는
..=
를 이용한 값의 범위 매칭- 값을 해체하여 분리하기
- 구조체 해체
- 열거형 해체
- 참조자 해체
- 구조체와 튜플 해체
- 패턴 내에서 값 무시하기
_
를 이용해 전체 값 무시- 중첩된
_
를 이용해 값의 일부 무시 - 언더스코어로 시작하는 이름을 이용해 쓰이지 않는 변수 무시
..
를 이용해 값의 나머지 부분 무시
ref
와ref mut
를 이용해 패턴 내에서 참조자 생성- 매치 가드를 이용한 추가 조건
- match 갈래 뒤에 추가로 붙는 if 조건
- 패턴 매칭과 해당 조건이 모두 만족되어야 해당 갈래가 선택
@
바인딩- at 연산자인
@
는 해당 값이 패턴과 매치되는지 확인하는 동시에 해당 값을 갖는 변수를 생성
- at 연산자인
예제 - 개요
- 코드
-
fn main() { let i = 1; match i { 0 => println!("1 : 0"), 1 => println!("1 : 1"), _ => println!("1 : _"), } let option: Option<i32> = Some(1); if let Some(_i) = option { println!("2 : {}", _i); } let option: Option<i32> = None; if let Some(_i) = option { println!("3 : {}", _i); } else { println!("3 : None"); } let option1: Option<i32> = None; let option2: Option<i32> = Some(1); if let Some(_i) = option1 { println!("4 : option1 - {}", _i); } else if let Some(_i) = option2 { println!("4 : option2 {}", _i); } let mut v = Vec::new(); v.push("a"); v.push("b"); v.push("c"); while let Some(_s) = v.pop() { println!("5 : {}", _s); } let v = vec!["a", "b", "c"]; for (index, value) in v.iter().enumerate() { println!("6 : {}, {}", index, value); } let (x, y, z) = (1, 2, 3); println!("7 : {}, {}, {}", x, y, z); let (x, _, z) = (1, 2, 3); println!("7 : {}, {}", x, z); }
-
- 실행 결과
-
1 : 1 2 : 1 3 : None 4 : option2 1 5 : c 5 : b 5 : a 6 : 0, a 6 : 1, b 6 : 2, c 7 : 1, 2, 3 7 : 1, 3
-
예제 - 리터럴 매칭
- 코드
-
fn main() { let x = 1; match x { 1 => println!("1"), 2 => println!("2"), _ => println!("_"), } }
-
- 실행 결과
-
1
-
예제 - 명명 변수 매칭
- 코드
-
fn main() { let x = Some(1); let y = 2; match x { Some(y) => println!("{}", y), None => println!("None"), } println!("{:?}, {}", x, y); }
-
- 실행 결과
-
1 Some(1), 2
-
예제 - 다중 패턴
- 코드
-
fn main() { let x = 1; match x { 1 | 2 => println!("1|2"), _ => println!("_"), } }
-
- 실행 결과
-
1|2
-
예제 - ..=
를 이용한 값의 범위 매칭
- 코드
-
fn main() { for i in 0..5 { match i { 1..=3 => println!("{}", i), _ => println!("_"), } } }
-
- 실행 결과
-
_ 1 2 3 _
-
예제 - 구조체 해체
- 코드
-
struct Test { x: i32, y: i32, } fn main() { let test = Test { x: 1, y: 2 }; let Test { x: a, y: b } = test; println!("1 : {}, {}", a, b); let Test { x: a, y: _ } = test; println!("2 : {}", a); match test { Test { x: 2, y: 3 } => println!("3 : 1"), Test { x: 1, y: _ } => println!("3 : 2"), Test { x: _, y: _ } => println!("3 : 3"), } }
-
- 실행 결과
-
1 : 1, 2 2 : 1 3 : 2
-
예제 - 열거형 해체
- 코드
-
enum Test { A, B { x: i32, y: i32 }, C(String), D(i32, i32, i32), } fn main() { let test = Test::D(1, 2, 3); match test { Test::A => println!("Test::A"), Test::B { x, y } => println!("{}, {}", x, y), Test::C(s) => println!("{}", s), Test::D(x, y, z) => println!("{}, {}, {}", x, y, z), } }
-
- 실행 결과
-
1, 2, 3
-
예제 - 참조자 해체
- 코드
-
struct Test { x: i32, y: i32, } fn main() { let v = vec![Test { x: 1, y: 2 }, Test { x: 2, y: 3 }]; let sum: i32 = v.iter().map(|&Test { x, y }| x + y).sum(); println!("{}", sum); }
-
- 실행 결과
-
8
-
예제 - 구조체와 튜플 해체
- 코드
-
struct Test { x: i32, y: i32, } fn main() { let ((a, b), Test { x, y }) = ((1, 2), Test { x: 3, y: 4 }); println!("{}, {}, {}, {}", a, b, x, y); }
-
- 실행 결과
-
1, 2, 3, 4
-
예제 - _
를 이용해 전체 값 무시
- 코드
-
fn func(_: i32, y: i32) { println!("y : {}", y); } fn main() { let i = 1; match i { 1 => println!("1"), _ => println!("_"), } func(1, 2); }
-
- 실행 결과
-
1 y : 2
-
예제 - 중첩된 _
를 이용해 값의 일부 무시
- 코드
-
fn main() { let a = Some(1); let b = Some(2); match (a, b) { (Some(_), Some(_)) => println!("1 : 1"), _ => println!("1 : 2"), } let a = Some(1); let b: Option<i32> = None; match (a, b) { (Some(_), Some(_)) => println!("2 : 1"), _ => println!("2 : 2"), } }
-
- 실행 결과
-
1 : 1 2 : 2
-
예제 - 언더스코어로 시작하는 이름을 이용해 쓰이지 않는 변수 무시
- 코드
-
fn main() { let x = 1; let _y = 2; }
-
- 실행 결과
-
warning: unused variable: `x` --> src/main.rs:2:9 | 2 | let x = 1; | ^ help: if this is intentional, prefix it with an underscore: `_x` | = note: `#[warn(unused_variables)]` on by default
-
예제 - ..
를 이용해 값의 나머지 부분 무시
- 코드
-
struct Test { a: i32, b: i32, c: i32, d: i32, } fn main() { let test = Test { a: 1, b: 2, c: 3, d: 4, }; let Test { a, .. } = test; println!("1 : {}", a); let Test { a, b, .. } = test; println!("2 : {}, {}", a, b); let Test { a, d, .. } = test; println!("3 : {}, {}", a, d); let Test { a, d, c, .. } = test; println!("4 : {}, {}, {}", a, c, d); let t = (1, 2, 3, 4, 5); match t { (first, .., last) => println!("5 : {}, {}", first, last), } }
-
- 실행 결과
-
1 : 1 2 : 1, 2 3 : 1, 4 4 : 1, 3, 4 5 : 1, 5
-
예제 - ref
와 ref mut
를 이용해 패턴 내에서 참조자 생성
- 코드
-
fn main() { let s = Some(String::from("a")); match s { Some(ref _s) => println!("1.1 : {}", _s), None => println!("1.1 : None"), } println!("1.2 : {:?}", s); let mut s = Some(String::from("a")); match s { Some(ref mut _s) => *_s = String::from("b"), None => println!("2.1 : None"), } println!("2.2 : {:?}", s); }
-
- 실행 결과
-
1.1 : a 1.2 : Some("a") 2.2 : Some("b")
-
예제 - 매치 가드를 이용한 추가 조건
- 코드
-
fn main() { let i = Some(3); match i { Some(_i) if _i > 1 => println!("1"), _ => println!("2"), } }
-
- 실행 결과
-
1
-
예제 - @
바인딩
- 코드
-
enum Test { A { x: i32 }, } fn main() { let test = Test::A { x: 3 }; match test { Test::A { x: _x @ 1..=5 } => println!("{}", _x), _ => println!("_"), } }
-
- 실행 결과
-
3
-