NeuroWhAI의 잡블로그

[Rust] 패턴 매칭 바인딩 모드(Binding Modes) 설명 본문

개발 및 공부/언어

[Rust] 패턴 매칭 바인딩 모드(Binding Modes) 설명

NeuroWhAI 2019. 1. 20. 14:12


주의! 공부하고 정리한 글이라서 틀린 부분이 있을 수 있습니다.

올바른 지적은 언제나 환영합니다.



Rust는 패턴 매칭시 적절한 바인딩 모드를 선택함으로써 프로그래머 입장에서 쉽게 패턴 매칭 코드를 작성할 수 있게 합니다.

다만 구버전 Rust까지는 그런 기능이 부족해서 아래 코드는 컴파일이 안되었었습니다.

let s: &Option<String> = &Some("hello".to_string());

match s {
    Some(s) => println!("s is: {}", s),
    _ => (),
};

s는 &Option<_>인데 패턴은 Option<_>이기 때문이었죠.

그래서 아래처럼 수정해야 했었습니다.

let s: &Option<String> = &Some("hello".to_string());

match s {
    &Some(ref s) => println!("s is: {}", s),
    _ => (),
};
패턴에 '&'를 붙혀 &Option<_>으로 바꿔주고
패턴의 s는 ref s로 바꿔 빌린 것을 이동시킬 수 없다는 에러가 뜨지 않도록 했습니다.
그러나!
Rust 2018 에디션부터는 첫번째 코드도 문제없이 컴파일이 됩니다!
컴파일러가 알아서 s의 타입이 &Option<_>인 것을 보고 s에 역참조를 수행한 후 ref 바인딩 모드를 선택하여 Some(s) 패턴을 Some(ref s)로 해석하기 때문이죠.

구체적으로 어떤 순서로 컴파일러가 바인딩 모드를 선택하는지 알아보겠습니다.
먼저 컴파일러가 자동으로 바인딩 모드를 선택할 수 있는 조건이 있습니다.

레퍼런스 값이 레퍼런스가 아닌 패턴과 매칭이 수행될 때만 자동으로 바인딩 모드가 선택됩니다.

'레퍼런스 값'이라는 것은 위 예시에서 s가 &Option<_>인 것처럼 레퍼런스 값이여야 한다는 것이고

'레퍼런스가 아닌 패턴'[각주:1]이라는 것은 와일드카드('_') 패턴과 레퍼런스 패턴을 제외한 모든 패턴을 말합니다.

그러니까 Some(s)는 레퍼런스가 아닌 패턴이고 &Some(s)는 레퍼런스인 패턴이라는 것이죠.

실제로 &Some(ref s)대신 &Some(s)라고 하면 바인딩 모드가 자동으로 선택되지 않아 오류[각주:2]가 발생합니다.

또한 패턴에 명시적인 ref, ref mut, mut 사용이 없어야 합니다.


다음으로 순서입니다.

v를 현재 패턴 매칭의 대상 값이라고 하겠습니다.

컴파일러는 패턴의 바깥에서 안으로 들어가는 순서로 패턴을 하나씩 확인합니다.

만약 패턴이 Some(Ok(42))라면 v가 일단 Some인지 확인하고 그 안의 것이 Ok인지 확인하고 또 그 안의 것이 42인지 확인한다는 소리죠.

현재 패턴이 '레퍼런스가 아닌 패턴'이라면 패턴의 타입과 맞춰질 때 까지 v에 역참조를 수행합니다.

이때 v의 레퍼런스가 immutable이었다면 바인딩 모드는 ref가 되며 mutable이었다면 ref mut이 됩니다.

단, 이전에 자동으로 선택된 바인딩 모드가 ref였다면 ref mut이 아니라 ref가 됩니다.

그도 당연한 것이 밖의 것은 immutable인데 안의 것이 mutable이면 이상하니까요.


써놓고 보니 어렵네요.

예시를 들어보겠습니다.

v가 &Some(42)이고 패턴이 Some(s)라고 합시다.

v가 레퍼런스 값이고 패턴은 레퍼런스가 아닌 패턴이므로 바인딩 모드가 자동으로 선택됩니다.

Some(s)은 레퍼런스가 아닌 패턴이므로 v에 역참조를 수행하여 Some(42)로 만듭니다.

이때 v는 immutable 레퍼런스였으므로 바인딩 모드는 ref가 됩니다.

그래서 실제 패턴은 Some(ref s)가 되어 s는 42를 immutable 참조하게 됩니다.


더 복잡한 예시도 들어보죠

v가 &Some(&mut Some(42))이고 패턴이 Some(Some(s))라고 합시다.

조건이 맞으므로 자동으로 바인딩 모드가 선택됩니다.

v에 역참조를 수행하여 Some(&mut Some(42))로 만들어 밖의 패턴을 충족시킵니다.

이어서 현재 바인딩 모드는 ref가 되며 v를 한 꺼풀 벗겨 임시로 &mut Some(42)로 합니다.

v에 또 역참조를 수행하여 Some(42)로 만드는데 mutable 레퍼런스였지만 이전 바인딩 모드가 ref였으므로 그대로 ref로 유지됩니다.

최종적으로 s는 ref가 되어 42를 immutable 참조하게 됩니다.



참고

https://doc.rust-lang.org/reference/patterns.html#binding-modes

https://rust-lang-nursery.github.io/edition-guide/rust-2018/ownership-and-lifetimes/default-match-bindings.html

  1. non-reference pattern [본문으로]
  2. error[E0507]: cannot move out of borrowed content [본문으로]


Comments