#31 패턴과 매칭

Pt J·2020년 9월 20일
0

[完] Rust Programming

목록 보기
34/41
post-thumbnail

이 시리즈는 Rust 공식문서를 통해 공부한 흔적임을 밝힙니다.

우리는 오래 전, 열거형에 대해 공부할 때 패턴 매칭에 대해 다룬 바가 있다.
열거형 외에도 패턴 매칭을 사용할 수 있는 상황은 더 존재한다.
이번 시간에는 이것에 대해 좀 더 자세히 알아보도록 하겠다.

패턴 Pattern

패턴은 어떤 값과 비교되어 그것이 일치하면 그 값을 코드에서 사용하는 용도로 이용된다.

match

우리가 열거형과 패턴 매칭에서 배웠던 것처럼 패턴을 match와 같이 사용할 수 있다.
match는 하나 이상의 arm으로 이루어져 있는데
arm은 패턴과 표현식으로 이루어진다는 것을 우리는 학습한 바 있다.
arm은 패턴 => 표현식의 형태를 띄고 있으며
match와 중괄호 블록 사이에 오는 어떤 값이 어떤 arm의 패턴과 일치한다면
해당 arm의 표현식이 실행된다.

match 표현식에서 유의해야 할 부분은 모든 경우의 수에 대한 arm이 존재해야 한다는 것이다.
모든 것을 일일이 열거할 수 없을 경우에는
맨 마지막에 모든 값에 일치하는 패턴을 넣어 나머지 경우를 모두 포함시킬 수 있다.
우리는 이것을 자리지정자라고 부른다는 것을 이미 배운 바가 있다.

match에 대한 예제는 충분히 다루어봤으므로 생략한다.

if let

if let에 대한 것도 열거형과 패턴 매칭 끝자락에 잠깐 다루었다.
match 표현식을 사용하고자 하는데 arm이 어떤 패턴과 자리지정자 둘 밖에 없다면
match 표현식보다 if let 표현식을 사용하는 게 더 짧고 가독성이 높다는 것이다.
if let의 중괄호 블록에는 해당 패턴과 일치하는 경우의 표현식을,
else의 중괄호 블록에는 그렇지 않은 경우의 표현식을 적어 넣는다.

우리는 단일 if let else 구문만 이야기했지만
if 표현식처럼 if let 표현식도 if let else if let else 와 같이 이어갈 수 있다.
심지어는 if let이 아니라 if와도 혼용할 수 있다.

이를 위한 간단한 예시로 몇 가지 조건을 검사하는 예제를 작성해보자.
예제의 단순화를 위해 값은 사용자에게 입력받지 않고 하드코딩 하였다.

peter@hp-laptop:~/rust-practice$ cd chapter18
peter@hp-laptop:~/rust-practice/chapter18$ cargo new if_let_example
     Created binary (application) `if_let_example` package
peter@hp-laptop:~/rust-practice/chapter18$ cd if_let_example/
peter@hp-laptop:~/rust-practice/chapter18/if_let_example$ vi src/main.rs

src/main.rs

fn main() {
    let favorite_color: Option<&str> = None;
    let is_tuesday = false;
    let age: Result<u8, _> = "34".parse();

    if let Some(color) = favorite_color {
        println!("Using your favorite color, {}, as the background", color);
    } else if is_tuesday {
        println!("Tuesday is green day!");
    }
    else if let Ok(age) = age {
        if age > 30 {
            println!("Using purple as the background color");
        } else {
            println!("Using orange as the background color");
        }
    } else {
        println!("Using blue as the background color");
    }
}
eter@hp-laptop:~/rust-practice/chapter18/if_let_example$ cargo run
   Compiling if_let_example v0.1.0 (/home/peter/rust-practice/chapter18/if_let_example)
    Finished dev [unoptimized + debuginfo] target(s) in 0.24s
     Running `target/debug/if_let_example`
Using purple as the background color
peter@hp-laptop:~/rust-practice/chapter18/if_let_example$ 

하지만 if let 표현식은 match와 달리 컴파일러가
모든 경우의 수에 대한 검사를 해주지 못하므로
안전성을 위해서는 if let이 복잡해진다면 if let 보다는 match를 사용하는 게 좋겠다.

while let

while let은 반복 조건이 패턴 일치인 반복문이다.
주어진 값이 주어진 패턴과 일치하는 경우에만 반복하고 일치하지 않아지면 반복을 종료한다.

간단한 예제를 살펴 보도록 하자.

peter@hp-laptop:~/rust-practice/chapter18/if_let_example$ cd ..
peter@hp-laptop:~/rust-practice/chapter18$ cargo new while_let_example
     Created binary (application) `while_let_example` package
peter@hp-laptop:~/rust-practice/chapter18$ cd while_let_example/
peter@hp-laptop:~/rust-practice/chapter18/while_let_example$ vi src/main.rs

src/main.rs

fn main() {
    let mut stack = Vec::new();

    stack.push(1);
    stack.push(2);
    stack.push(3);

    while let Some(top) = stack.pop() {
        println!("{}", top);
    }
}
peter@hp-laptop:~/rust-practice/chapter18/while_let_example$ cargo run
   Compiling while_let_example v0.1.0 (/home/peter/rust-practice/chapter18/while_let_example)
    Finished dev [unoptimized + debuginfo] target(s) in 0.21s
     Running `target/debug/while_let_example`
3
2
1
peter@hp-laptop:~/rust-practice/chapter18/while_let_example$

pop 메서드는 벡터가 비어있지 않다면 마지막 원소를 Some에 넣어 반환하고
비어있다면 None을 반환한다.
그리고 이것이 None이 되면 while let의 조건이 충족되지 않아 반복을 종료한다.

for

우리가 그저 반복문이라고만 알고 있던 for 문에도 패턴을 적용할 수 있다.
for 아이템 in 반복자 형태에서 아이템에 패턴이 들어간다.
반복자의 아이템이 패턴에 일치해야만 컴파일 오류가 발생하지 않는다.

우리는 평소에 for 문을 다음과 같이 사용했다.

peter@hp-laptop:~/rust-practice/chapter18/while_let_example$ cd ..
peter@hp-laptop:~/rust-practice/chapter18$ cargo new for_example
     Created binary (application) `for_example` package
peter@hp-laptop:~/rust-practice/chapter18$ cd for_example/
peter@hp-laptop:~/rust-practice/chapter18/for_example$ vi src/main.rs

src/main.rs

fn main() {
    let v = vec!['a', 'b', 'c'];

    for (index, value) in v.iter().enumerate() {
        println!("{} is at index {}", value, index);
    }
}
peter@hp-laptop:~/rust-practice/chapter18/for_example$ cargo run
   Compiling for_example v0.1.0 (/home/peter/rust-practice/chapter18/for_example)
    Finished dev [unoptimized + debuginfo] target(s) in 0.20s
     Running `target/debug/for_example`
a is at index 0
b is at index 1
c is at index 2
peter@hp-laptop:~/rust-practice/chapter18/for_example$ 

튜플 형태의 패턴에 아이템의 튜플이 매치되어 각각을 변수처럼 사용할 수 있다.

let

let은 단순히 변수를 선언하거나 shadowing하기 위한 것으로 인식하고 있었지만
이 녀석도 패턴과 유관하다.
let 패턴 = 표현식; 형태로 값이 대입되기 때문이다.
패턴으로 변수 이름 하나가 오면 그것은 어떤 값이든 담을 수 있기에 그대로 대입된다.
표현식의 결과값이 튜플일 경우 하나의 튜플 변수에 저장할 수도 있지만
다음과 같이 패턴을 통해 각각의 변수로 분할하여 해체할 수도 있다.

let (x, y, z) = (1, 2, 3);

물론 원소의 개수가 맞지 않다면 패턴이 일치하지 않아 컴파일되지 않는다.
만약 부분적으로만 사용하고 싶다면 자리지정자 _를 통해 특정 값을 무시하거나
..를 통해 특정 구간을 무시할 수 있다.
여기서 주의할 점은, _는 여러 번 사용할 수 있지만
..는 여러 번 사용하게 될 경우 어느 부분을 의미하는지 불명확해지므로 허용하지 않는다.
자리지정자는 원래 '어떤 값이든 매치됨'을 의미하지만
'어떤 값이 와도 신경쓰지 않겠다'의 의미로 사용되었다고 할 수 있겠다.

매개변수

함수의 매개변수에도 패턴이 적용된다.
이것은 let에서와 크게 다르지 않다.
매개변수가 x: i32라면 그 어떤 i32 값이든 패턴에 일치하여 전달될 수 있다.

그리고 let에서와 마찬가지로 패턴을 이용하여 튜플을 해체할 수 있다.

peter@hp-laptop:~/rust-practice/chapter18/for_example$ cd ..
peter@hp-laptop:~/rust-practice/chapter18$ cargo new parameter_example
     Created binary (application) `parameter_example` package
peter@hp-laptop:~/rust-practice/chapter18$ cd parameter_example/
peter@hp-laptop:~/rust-practice/chapter18/parameter_example$ vi src/main.rs

src/main.rs

fn print_coordinates(&(x, y): &(i32, i32)) {
    println!("Current location: ({}, {})", x, y);
}

fn main() {
    let point = (3, 5);
    print_coordinates(&point);
}
peter@hp-laptop:~/rust-practice/chapter18/parameter_example$ cargo run
   Compiling parameter_example v0.1.0 (/home/peter/rust-practice/chapter18/parameter_example)
    Finished dev [unoptimized + debuginfo] target(s) in 0.20s
     Running `target/debug/parameter_example`
Current location: (3, 5)
peter@hp-laptop:~/rust-practice/chapter18/parameter_example$ 

이를 통해 인자로 전달할 땐 튜플로 받되,
함수에서 사용할 땐 튜플의 인덱스로 접근하는 게 아니라 개별 변수로 접근하도록 할 수 있다.

부인할 수 있는/없는 패턴

패턴은 크게 두 가지 부류로 나눌 수 있다.
부인할 수 있는Refutable 패턴과 부인할 수 없는Irrefutable 패턴.
let x = 5;에서와 같이 주어진 모든 값에 일치하는 패턴을 부인할 수 없는 패턴이라고 한다.
반대로, match 문에서처럼 특정 값들에 대해서만 일치하는 패턴을 부인할 수 있는 패턴이라고 한다.

match, if let, while let에서는 부인할 수 있는 패턴을 사용해야 한다.
부인할 수 없는 패턴을 사용해도 문제가 되지는 않지만 이걸 사용하는 의미가 없게 된다.
for, let, 매개변수에서는 부인할 수 없는 패턴을 사용해야 한다.
부인할 수 있는 패턴을 사용하게 될 경우 Rust 컴파일러가 컴파일을 거부할 것이다.

패턴 문법

우리는 지금까지 패턴을 사용할 수 있는 상황들에 대해 알아보았다.
그렇다면 그 패턴 자체는 어떻게 생긴 녀석이어야 하는지 알아보도록 하자.
다음의 패턴 문법들은 모든 상황에서 다 가능한 건 아니고, 상황에 맞게 사용할 수 있다.

리터럴과의 매칭

패턴으로 리터럴을 사용하여 리터럴과 어떤 값을 비교하도록 할 수 있다.
예를 들어, 다음과 같은 경우다.

peter@hp-laptop:~/rust-practice/chapter18/parameter_example$ cd ..
peter@hp-laptop:~/rust-practice/chapter18$ cargo new literal_match
     Created binary (application) `literal_match` package
peter@hp-laptop:~/rust-practice/chapter18$ cd literal_match/
peter@hp-laptop:~/rust-practice/chapter18/literal_match$ vi src/main.rs

src/main.rs

fn main() {
    let x = 1;

    match x {
        1 => println!("one"),
        2 => println!("two"),
        3 => println!("three"),
        _ => println!("anything"),
    }
}
peter@hp-laptop:~/rust-practice/chapter18/literal_match$ cargo run
   Compiling literal_match v0.1.0 (/home/peter/rust-practice/chapter18/literal_match)
    Finished dev [unoptimized + debuginfo] target(s) in 0.17s
     Running `target/debug/literal_match`
one
peter@hp-laptop:~/rust-practice/chapter18/literal_match$ 

그림자 변수와의 매칭

패턴에 열거형이 사용될 경우,
그 열거값이 포함하고 있는 개별 값에 대해서도 패턴을 설정할 수 있다.
예를 들어, Option의 경우, Some인 경우와 None인 경우로 나눌 수도 있지만
Some(x)x에 적절한 값을 넣어
Some이면서 특정 값을 가지고 있는 경우로 패턴을 설정할 수도 있다.
그리고 Some(x)와 같이 변수를 넣을 경우, 이것은 그림자 변수로 사용된다.
그림자 변수는 패턴 매칭 내부에서만 사용되는 지역 변수와 같다.
외부에 같은 이름의 변수가 있어도 그것과는 별개로 사용되며
열거값에 포함되어 있는 값을 이 패턴과 연결된 표현식에서 사용할 수 있게 해준다.

다음은 그림자 변수가 포함된 match 표현식의 예시다.

peter@hp-laptop:~/rust-practice/chapter18/literal_match$ cd ..
peter@hp-laptop:~/rust-practice/chapter18$ cargo new shadow_match
     Created binary (application) `shadow_match` package
peter@hp-laptop:~/rust-practice/chapter18$ cd shadow_match/
peter@hp-laptop:~/rust-practice/chapter18/shadow_match$ 

src/main.rs

fn main() {
    let x = Some(5);
    let y = 10;

    match x {
        Some(50) => println!("Got 50"),
        Some(y) => println!("Matched, y = {:?}", y),
        _ => println!("Default case, x = {:?}", x),
    }

    println!("at the end: x = {:?}, y = {:?}", x, y);
}
peter@hp-laptop:~/rust-practice/chapter18/shadow_match$ cargo run
   Compiling shadow_match v0.1.0 (/home/peter/rust-practice/chapter18/shadow_match)
    Finished dev [unoptimized + debuginfo] target(s) in 0.34s
     Running `target/debug/shadow_match`
Matched, y = 5
at the end: x = Some(5), y = 10
peter@hp-laptop:~/rust-practice/chapter18/shadow_match$ 

실제로 Some(y)는 앞서 선언한 y가 아니라 그림자 변수임을 확인할 수 있다.
그리고 match 문을 벗어난 후의 y는 다시 앞서 선언한 y가 출력된다.

다중 패턴

여러 개의 패턴에 대한 실행 표현식이 동일하다면 해당 패턴들을 묶어서 다중 패턴으로 사용할 수 있다.
이 때, 각각의 패턴은 |로 구분된다.
이것은 OR 연산으로, 해당 패턴들 중 하나라도 일치하면 일치하는 것으로 취급한다.

다중 패턴은 다음과 같이 사용할 수 있다.

peter@hp-laptop:~/rust-practice/chapter18/shadow_match$ cd ..peter@hp-laptop:~/rust-practice/chapter18$ cargo new multiple_patterns
     Created binary (application) `multiple_patterns` package
peter@hp-laptop:~/rust-practice/chapter18$ cd multiple_patterns/
peter@hp-laptop:~/rust-practice/chapter18/multiple_patterns$ vi src/main.rs

src/main.rs

fn main() {
    let x = 1;

    match x {
        1 | 2 => println!("one or two"),
        3 => println!("three"),
        _ => println!("anything"),
    }
}
peter@hp-laptop:~/rust-practice/chapter18/multiple_patterns$ cargo run
   Compiling multiple_patterns v0.1.0 (/home/peter/rust-practice/chapter18/multiple_patterns)
    Finished dev [unoptimized + debuginfo] target(s) in 0.19s
     Running `target/debug/multiple_patterns`
one or two
peter@hp-laptop:~/rust-practice/chapter18/multiple_patterns$ 

x의 값을 2로 변경해도 같은 결과가 나온다.

만약 연속된 일련의 값을 다중 패턴으로 사용하고 싶다면 ..=을 사용할 수 있다.
예를 들어, 1 | 2 | 3 | 4 | 5라고 쓸 것을 1 ..= 5라고 할 수 있다는 것이다.
// 예전에는 ...로 사용했기에 오래된 자료에는 ...로 나와 있을 수 있다.
다만, 이것은 정수와 문자에서만 사용할 수 있다.
match 문을 사용할 때 컴파일러가 커버되지 않는 값이 있는지 검사하는데
범위가 비어있는지 판단 가능한 것이 이들 뿐이기 때문이다.

값 해체하기

앞에서 let과 매개변수에서 패턴을 사용하는 것을 살펴볼 때 이미 확인했지만
묶여 있는 값을 패턴을 이용하여 해체할 수 있다.
우리는 튜플을 해체하는 것만 해봤지만 구조체를 해체하는 것도 크게 다르지 않다.

peter@hp-laptop:~/rust-practice/chapter18/multiple_patterns$ cd ..
peter@hp-laptop:~/rust-practice/chapter18$ cargo new destructure_struct
     Created binary (application) `destructure_struct` package
peter@hp-laptop:~/rust-practice/chapter18$ cd destructure_struct/
peter@hp-laptop:~/rust-practice/chapter18/destructure_struct$ vi src/main.rs

src/main.rs

struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let p = Point { x: 0, y: 7 };

    let Point { x: a, y: b } = p;

    assert_eq!(0, a);
    assert_eq!(7, b);
}
peter@hp-laptop:~/rust-practice/chapter18/destructure_struct$ cargo run
   Compiling destructure_struct v0.1.0 (/home/peter/rust-practice/chapter18/destructure_struct)
    Finished dev [unoptimized + debuginfo] target(s) in 0.20s
     Running `target/debug/destructure_struct`
peter@hp-laptop:~/rust-practice/chapter18/destructure_struct$ 

만약 구조체의 필드와 같은 이름을 가진 변수를 사용하면 이보다 더 단순하게 사용할 수도 있다.
필드와 변수의 이름이 같다면 let Point { x, y } = p;와 같이 사용할 수 있다.

혹은, 이것을 변수에 대입하지 않고 바로 match 문으로 연결할 수도 있다.

peter@hp-laptop:~/rust-practice/chapter18/destructure_struct$ cd ..
peter@hp-laptop:~/rust-practice/chapter18$ cargo new destructure_match     Created binary (application) `destructure_match` package
peter@hp-laptop:~/rust-practice/chapter18$ cd destructure_match/
peter@hp-laptop:~/rust-practice/chapter18/destructure_match$ vi src/main.rs

src/main.rs

struct Point {
    x :i32,
    y: i32,
}

fn main() {
    let p = Point { x: 0, y: 7 };

    match p {
        Point { x, y: 0 } => println!("On the x axis at {}", x),
        Point { x: 0, y } => println!("On the y axis at {}", y),
        Point { x, y } => println!("On neigher axis: ({}, {})", x, y),
    }
}
peter@hp-laptop:~/rust-practice/chapter18/destructure_match$ cargo run
   Compiling destructure_match v0.1.0 (/home/peter/rust-practice/chapter18/destructure_match)
    Finished dev [unoptimized + debuginfo] target(s) in 0.18s
     Running `target/debug/destructure_match`
On the y axis at 7

또한, 패턴을 통해 열거형도 해체할 수 있는데
열거형의 경우 각각의 열거값을 패턴으로 하여 match 문을 작성할 수 있다.
그리고 그 match 문의 arm에서 해당 패턴에 대한 값 해체가 이루어진다.

peter@hp-laptop:~/rust-practice/chapter18/destructure_match$ cd ..
peter@hp-laptop:~/rust-practice/chapter18$ cargo new destructure_enum
     Created binary (application) `destructure_enum` package
peter@hp-laptop:~/rust-practice/chapter18$ cd destructure_enum/
peter@hp-laptop:~/rust-practice/chapter18/destructure_enum$ vi src/main.rs

src/main.rs

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

fn main() {
    let msg = Message::ChangeColor(0, 160, 255);

    match msg {
        Message::Quit => 
            println!("The Quit variant has no data to destructure."), 
        Message::Move { x, y } =>
            println!("Move in the x direction {} and in the y direction {}", x, y),
        Message::Write(text) => 
            println!("Text message: {}", text),
        Message::ChangeColor(r, g, b) => 
            println!("Change the color to red {}, green {}, and blue {}", r, g, b),
    }
}
peter@hp-laptop:~/rust-practice/chapter18/destructure_enum$ cargo run
   Compiling destructure_enum v0.1.0 (/home/peter/rust-practice/chapter18/destructure_enum)
   
# snip warnings

    Finished dev [unoptimized + debuginfo] target(s) in 0.23s
     Running `target/debug/destructure_enum`
Change the color to red 0, green 160, and blue 255
peter@hp-laptop:~/rust-practice/chapter18/destructure_enum$ 

각각의 열거값마다 포함하고 있는 값들의 구성이 서로 다를 수 있기 때문에
반드시 이렇게 열거값에 따른 패턴 매칭 후에 각각의 경우에 대해 해체해주어야 한다.

만약 열거형 안에 열거형이 중첩되어 있다면
외부 열거형을 열거값으로 패턴 매칭 후 그 표현식에서 내부 열거형을 열거값으로 패턴매칭하거나
애초부터 외부열거값(내부열거값)을 패턴으로 사용할 수 있다.

패턴 값 무시

앞서 몇 번 언급된 바가 있지만 자리지정자 _를 통해 패턴의 일부를 무시할 수 있다.
그리고 ..를 통해 어떤 값 이전 전체, 어떤 값 이후 전체,
혹은 어떤 두 값 사이를 범위째 무시할 수 있다는 것과
그것을 두 번 이상 사용할 경우 어느 부분을 나타내는지 모호해져서 최대 한 번만 가능함도 배웠다.

자리지정자를 사용하면 값을 바인딩하지도 않고 무시하지만
변수 이름을 정한 채 그 앞에 _를 붙이면 값을 바인딩한 채 그것을 무시하게 된다.
이것이 왜 필요한가 싶을 수 있는데,
Rust는 변수를 선언해놓고 사용하지 않으면 경고를 띄운다.
그런데 때로는 코드의 프로토타입에서 나중에 사용할 변수를 선언해두었지만
아직은 사용하지 않은 상황이 있을 수 있다.
이런 상황에서 우리는 불필요한 경고 없이 컴파일 결과를 확인하기 위해 임시로 _를 붙일 수 있다.
물론 개발이 완료되면 앞에 _가 붙은 변수는 더이상 남지 않을 것이다.

매치 가드

매치 가드는 패턴을 보완하여 추가적인 조건을 설정하는 데 사용된다.
이것을 사용하면 패턴만으로는 표현할 수 없는, 보다 복잡한 조건의 매칭이 가능하다.

예를 들어, 패턴만으로는 값의 범위를 제한할 수 없지만
매치 가드를 사용하면 다음과 같이 작성할 수 있다.

peter@hp-laptop:~/rust-practice/chapter18/destructure_enum$ cd ..
peter@hp-laptop:~/rust-practice/chapter18$ cargo new match_guard
     Created binary (application) `match_guard` package
peter@hp-laptop:~/rust-practice/chapter18$ cd match_guard/
peter@hp-laptop:~/rust-practice/chapter18/match_guard$ vi src/main.rs

src/main.rs

fn main() {
    let num = Some(4);

    match num {
        Some(x) if x < 5 => println!("less than five: {}", 4),
        Some(x) => println!("{}", x),
        None => (),
    }
}
peter@hp-laptop:~/rust-practice/chapter18/match_guard$ cargo run
   Compiling match_guard v0.1.0 (/home/peter/rust-practice/chapter18/match_guard)
    Finished dev [unoptimized + debuginfo] target(s) in 0.26s
     Running `target/debug/match_guard`
less than five: 4
peter@hp-laptop:~/rust-practice/chapter18/match_guard$ 

또한, 매치 가드를 사용하면 match 문 밖의 값과의 비교 연산도 가능하다.

peter@hp-laptop:~/rust-practice/chapter18/match_guard$ cd ..
peter@hp-laptop:~/rust-practice/chapter18$ cargo new match_guard_outer
     Created binary (application) `match_guard_outer` package
peter@hp-laptop:~/rust-practice/chapter18$ cd match_guard_outer/
peter@hp-laptop:~/rust-practice/chapter18/match_guard_outer$ vi src/main.rs

src/main.rs

fn main() {
    let x = Some(5);
    let y = 10;

    match x {
        Some(50) => println!("50"),
        Some(n) if n == y => println!("Matched, n = {}", n),
        _ => println!("Default case, x = {:?}", x),
    }

    println!("at the end: x = {:?}, y = {}", x, y);
}
peter@hp-laptop:~/rust-practice/chapter18/match_guard_outer$ cargo run
   Compiling match_guard_outer v0.1.0 (/home/peter/rust-practice/chapter18/match_guard_outer)
    Finished dev [unoptimized + debuginfo] target(s) in 0.21s
     Running `target/debug/match_guard_outer`
Default case, x = Some(5)
at the end: x = Some(5), y = 10
peter@hp-laptop:~/rust-practice/chapter18/match_guard_outer$ 

매치 가드는 패턴과는 달리 새로운 변수를 생성하지 않고
패턴이 만든 그림자 변수나 외부 변수를 사용한다.
다중 패턴에 매치 가드를 적용할 경우 다중 패턴 모두에 매치 가드가 적용된다.
만약 다중 패턴을 사용하면서 그 중 특정 값에만 적용하고 싶다면
4 | 5 | (6 if y)와 같이 괄호로 묶어주어야 한다.

@ 바인딩

@ 연산자를 사용하면 패턴 일치 여부를 확인하면서 그 값을 가진 변수를 생성할 수 있다.
이 변수는 해당 arm의 표현식에서 사용할 수 있다.
패턴 앞에 변수 @을 붙여서 사용한다.

다음은 @ 바인딩을 사용하는 예제다.

peter@hp-laptop:~/rust-practice/chapter18/match_guard_outer$ cd ..
peter@hp-laptop:~/rust-practice/chapter18$ cargo new at_binding
     Created binary (application) `at_binding` package
peter@hp-laptop:~/rust-practice/chapter18$ cd at_binding/
peter@hp-laptop:~/rust-practice/chapter18/at_binding$ vi src/main.rs

src/main.rs

enum Message {
    Hello { id: i32 },
}

fn main() {
    let msg = Message::Hello { id: 5 };

    match msg {
        Message::Hello { id: id_variable @ 3 ..= 7 } =>
            println!("Found an id in range: {}", id_variable),
        Message::Hello { id: 10 ..= 12 } =>
            println!("Found an id in another range"),
        Message::Hello { id } =>
            println!("Found some other id: {}", id),
    }
}
peter@hp-laptop:~/rust-practice/chapter18/at_binding$ cargo run
   Compiling at_binding v0.1.0 (/home/peter/rust-practice/chapter18/at_binding)
    Finished dev [unoptimized + debuginfo] target(s) in 0.19s
     Running `target/debug/at_binding`
Found an id in range: 5
peter@hp-laptop:~/rust-practice/chapter18/at_binding$ 

Some(x)와 같이 그것이 가진 모든 값을 저장하여 표현식에서 x를 사용할 수 있다면
@ 바인딩이 필요하지 않지만
열거형에 대한 match 문에서 열거형 자체가 아닌 세부 패턴으로 조건을 제시해서
그 열거형 자체에 대한 참조가 불가능할 때 @ 바인딩이 유용하다.

패턴과 매칭은 이렇게 다양한 영역에서 다양하게 활용될 수 있다.
이것을 충분히 활용할 수 있게 되면 Rust를 보다 유연하게 사용할 수 있게 될 것이다.

이 포스트의 내용은 공식문서의 18장 Patterns and Matching에 해당합니다.

profile
Peter J Online Space - since July 2020

0개의 댓글