Updated:

7 minute read

개요

  • 단순하거나 복잡한 타입의 구조에 값들을 비교하기 위한 문법
  • 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가 패턴)
  • 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 letwhile let은 반증 가능 패턴만 허용
      • 성공 여부에 따라 다른 행동을 하도록 설계가 되었으므로 실패의 여지가 있는 패턴이 올 것을 가정
  • 패턴 구문
    • 리터럴 매칭
    • 명명 변수 매칭
      • 명명 변수는 어떠한 값에도 매칭되는 반증 불가능한 패턴
    • 다중 패턴
      • match 표현 내에서 or 을 뜻하는 | 구문을 이용해 여러개의 패턴과 매치 가능
    • ..=를 이용한 값의 범위 매칭
    • 값을 해체하여 분리하기
      • 구조체 해체
      • 열거형 해체
      • 참조자 해체
      • 구조체와 튜플 해체
    • 패턴 내에서 값 무시하기
      • _를 이용해 전체 값 무시
      • 중첩된 _를 이용해 값의 일부 무시
      • 언더스코어로 시작하는 이름을 이용해 쓰이지 않는 변수 무시
      • ..를 이용해 값의 나머지 부분 무시
    • refref mut를 이용해 패턴 내에서 참조자 생성
    • 매치 가드를 이용한 추가 조건
      • match 갈래 뒤에 추가로 붙는 if 조건
      • 패턴 매칭과 해당 조건이 모두 만족되어야 해당 갈래가 선택
    • @ 바인딩
      • 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
      


예제 - refref 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