Rust: 패턴
개요
- 단순하거나 복잡한 타입의 구조에 값들을 비교하기 위한 문법
- 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 키워드 바로 다음에 오는 값이 패턴(
letlet 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