7 분 소요

개요

  • 단순하거나 복잡한 타입의 구조에 값들을 비교하기 위한 문법
  • 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