
안녕하세요.
오늘은 Rust의 패턴 바인딩 (Pattern Binding) 에 대해 알아보겠습니다.
서론
Rust를 공부하다가 다음과 같은 코드를 봤습니다.
fn main() {
let x = Some(5);
let y = 15;
match x {
Some(y) if y == 5 => println!("A, inner y: {}", y),
_ => println!("B"),
}
println!("x, y: {}, {}", x.unwrap(), y);
}
처음에는 바깥의 y(=15) 와 비교한다고 생각했지만,
실제 출력은 다음과 같았습니다.
A, inner y: 5
x, y: 5, 15
예상과 달랐습니다.
이를 계기로 Rust의 패턴 바인딩 (Pattern Binding) 개념을 알게 되었습니다.
패턴 바인딩이란?
패턴 바인딩이란,
패턴에 매칭된 값을 새로운 변수에 연결(bind)하는 것
입니다.
다음 예제를 보겠습니다.
let x = Some(5);
match x {
Some(y) => println!("{}", y),
_ => {}
}
여기서 Some(y)는 단순히
“Some 안에 값이 있는가?” 를 검사하는 것이 아닙니다
의미는 다음과 같습니다
- x가 Some인지 확인한다.
- 내부 값을 꺼낸다.
- 그 값을 새로운 변수 y에 바인딩한다. (y는 5가 됨)
즉, 이 코드는 ‘값 비교’가 아니라 ‘구조 분해 + 변수 생성’ 입니다
Rust에서 식별자는 기본적으로 ‘항상 새로운 변수 바인딩’ 입니다
따라서 Some(y)는 비교가 아니라:
Some(inner_value)
와 동일합니다.
그리고 내부적으로는 다음과 같은 동작이 이루어집니다.
let y = inner_value;
이때 기존에 같은 이름의 변수가 있다면, 그 변수는 가려지게 됩니다.
이것을 Shadowing 이라고 합니다.
패턴의 종류와 바인딩
패턴은 크게 다음과 같이 나눌 수 있습니다
1) 리터럴 패턴 (값 비교)
Some(50)
이 경우에는 실제로 값 50과 비교합니다
2) 변수 패턴 (바인딩)
Some(y)
항상 새로운 변수 바인딩입니다.
3) 와일드카드 패턴
_
값을 무시합니다. 바인딩하지 않습니다.
4) 구조 분해 패턴
struct Point { x: i32, y: i32 }
let p = Point { x: 1, y: 2 };
match p {
Point { x, y } => println!("{} {}", x, y),
}
여기서 x, y 역시 새로 바인딩 됩니다.
Other Cases
패턴 바인딩은 match 에서만 일어나진 않습니다
아래 경우도 전부 바인딩 입니다
1) let
let (a, b) = (1, 2);
2) if let
if let Some(v) = x {
println!("{}", v);
}
3) while let
while let Some(v) = iterator.next() {
println!("{}", v);
}
4) 함수 파라미터
fn print_point((x, y): (i32, i32)) {
println!("{} {}", x, y);
}
바인딩과 소유권 (Move / Borrow)
패턴 바인딩은 소유권과도 밀접한 관련이 있습니다.
let x = Some(String::from("hello"));
match x {
Some(s) => println!("{}", s),
None => {}
}
여기서 s는
- String 타입 이므로 소유권을 가져옵니다 (move)
- 따라서 이후에 x를 사용할 수 없습니다
error[E0277]: `Option<String>` doesn't implement `std::fmt::Display`
--> src\\main.rs:14:20
|
14 | println!("{}", x);
| -- ^ `Option<String>` cannot be formatted with the default formatter
| |
| required by this formatting parameter
|
= help: the trait `std::fmt::Display` is not implemented for `Option<String>`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
만약 값을 소비하지 않고 참조만 하고 싶다면:
match &x {
Some(s) => println!("{}", s),
None => {}
}
- 이 경우 s는 &String 타입이 되며,
- borrow가 발생합니다.
Match Guard
바깥 변수를 비교하고 싶다면, 다음과 같이 사용할 수 있습니다
let x = Some(10);
let y = 10;
match x {
Some(n) if n == y => println!("equal"),
_ => {}
}
- n은 새로 바인딩된 변수
- if n == y 에서 바깥 변수 y를 사용
이것을 match guard 라고 합니다
@ 패턴
바인딩과 패턴 매칭을 동시에 하고 싶다면 @를 사용할 수 있습니다.
match x {
val @ Some(5) => println!("{:?}", val),
_ => {}
}
- x를 Some(5)와 매칭
- 전체 값을 val에 바인딩
그래서 다음 코드에서 출력 값은 이렇게 나옵니다
let x = Some(6);
match x {
val @ Some(5) => println!("equals to 5, {:?}", val),
val @ Some(6) => println!("equals to 6, {:?}", val),
_ => println!("else branch")
}
equals to 6, Some(6)
정리
다시 처음 코드로 돌아가 보면
fn main() {
let x = Some(5);
let y = 15;
match x {
Some(y) if y == 5 => println!("A, inner y: {}", y),
_ => println!("B"),
}
println!("x, y: {}, {}", x.unwrap(), y);
}
- Some(y) 에서 y는 새로 바인딩 됩니다. (y=5)
- if y == 5는 바인딩 된 y를 검사합니다.
- match 블록이 끝나면, 바인딩된 y는 스코프를 벗어납니다.
- 바깥 y는 여전히 15입니다.
따라서 출력이 이렇게 나옵니다.
A, inner y: 5
x, y: 5, 15
패턴 바인딩은
- 값 비교가 아니라 구조 분해입니다.
- 식별자는 기본적으로 새로운 변수 바인딩입니다.
- 기존 변수와 이름이 같다면 shadowing이 발생합니다.
- 소유권(move/borrow)과 직접 연결됩니다.
- match뿐 아니라 let, if let, 함수 파라미터 등 다양한 곳에서 사용됩니다.
감사합니다.
'Rust' 카테고리의 다른 글
| [Rust] 실제 코드로 확인해보는 Monomorphization (0) | 2026.02.18 |
|---|---|
| [Rust] Generic Lifetime (0) | 2026.02.16 |
| [Rust] unwrap 이해하기 (0) | 2026.02.14 |
| [Rust] 동시성 (0) | 2025.04.27 |
| [Rust] 컬렉션 (Collection) (0) | 2025.04.22 |