Rust 에러 E0530: match bindings cannot shadow tuple variants (튜플 배리언트 가리기)
E0530은 패턴에 쓴 이름이 이미 다른 데 쓰이고 있다는 뜻이에요. 보통 Rust가 새 변수를 만드는 자리에 Circle 같은 이름을 그냥 썼는데, 이 스코프에서 Circle은 이미 다른 것을 뜻해요. 튜플 배리언트, 튜플 구조체, 또는 static이요. Rust는 그 이름을 새 변수 뒤에 슬쩍 가려 버리지 않아요. 그게 내가 원한 일일 리가 거의 없으니까요. 대표적인 경우는 use Shape::*; 다음에 match 갈래를 Circle =>라고 쓴 거예요. 해결은 보통 두 글자면 돼요. Circle(..)이요. 이 페이지는 Rust 컴파일러 에러 해설의 일부예요.
에러 메시지
섹션 제목: “에러 메시지”error[E0530]: match bindings cannot shadow tuple variants --> src/main.rs:10:9 | 6 | use Shape::*; | -------- the tuple variant `Circle` is imported here...10 | Circle => "원", // ❌ `Circle`은 값을 담고 있어요 | ^^^^^^ | | | cannot be named the same as a tuple variant | help: try specify the pattern arguments: `Circle(..)`이 에러를 내는 프로그램은 다음과 같아요.
enum Shape { Circle(f64), Square(f64),}
use Shape::*;
fn name(shape: &Shape) -> &'static str { match shape { Circle => "원", // ❌ `Circle`은 값을 담고 있어요 Square(_) => "정사각형", }}
fn main() { println!("{}", name(&Circle(1.0)));}무슨 뜻인가요
섹션 제목: “무슨 뜻인가요”^^^는 10번째 줄의 Circle을 가리키고, 6번째 줄의 ---는 그 이름이 어디서 왔는지 보여 줘요. use Shape::*;가 배리언트 Shape::Circle을 스코프로 가져왔어요. 라벨은 “튜플 배리언트와 같은 이름을 붙일 수 없다”는 뜻이고, help:는 “패턴 인자를 적어 보라: Circle(..)“라고 제안해요.
패턴 안에서 x나 other 같은 그냥 이름은 새 변수를 만들어요. 그 변수는 무엇이든 매칭돼요. 그래서 Circle =>는 두 가지로 읽힐 수 있어요.
- “배리언트
Circle과 매칭하라”. 하지만Circle은f64를 담고 있으니, 올바른 패턴에는 괄호가 필요해요.Circle(_)나Circle(..)처럼요. - “무엇이든 매칭하고 그걸
Circle이라고 부르라”. 배리언트를 가려 버리는 새 변수예요.
Rust는 추측하지 않아요. 제목 줄 끝부분은 어떤 종류의 이름과 부딪혔는지 알려 주고, 첫 단어는 어디서 부딪혔는지 알려 줘요.
match bindings cannot shadow ...:match갈래에서let bindings cannot shadow ...:let이나if let에서function parameters cannot shadow ...: 함수의 매개변수 목록에서
그리고 끝은 tuple variants(튜플 배리언트), tuple structs(튜플 구조체), statics(static) 중 하나예요.
강아지 이름을 “선생님”이라고 지은 것과 비슷해요. 병원 대기실에서 누가 “선생님!” 하고 부를 때마다, 누구를 부르는 건지 아무도 몰라요.
Rust가 이렇게 하는 이유
섹션 제목: “Rust가 이렇게 하는 이유”이 규칙이 없다면 Circle =>는 Circle이라는 새 변수가 될 거예요. 그 변수는 모든 값과 매칭되고, 그 뒤의 갈래는 전부 절대 실행되지 않는 코드가 돼요. 프로그램은 컴파일되지만, 정사각형에도 “원”을 출력해요. 겉보기엔 맞는 코드 뒤에 숨은 고약한 버그예요.
그래서 Rust는 새 변수가 튜플 배리언트, 튜플 구조체, static의 이름을 가져가려고 하면 언제나 에러로 처리해요. 괄호를 붙여서 뜻을 분명히 하거나, 다른 이름을 고르면 돼요. None 같은 유닛 배리언트와 상수(const)는 달라요. 이것들은 그 자체로 올바른 패턴이라서, Rust는 “이 값과 매칭하라”로 읽어요. 패턴 매칭과 열거형을 참고하세요.
고치는 방법
섹션 제목: “고치는 방법”방법 1: (..)나 (_)를 붙여서 배리언트와 매칭하기
섹션 제목: “방법 1: (..)나 (_)를 붙여서 배리언트와 매칭하기”메시지에 tuple variants나 tuple structs가 있을 때 이 방법을 고르세요. 가장 흔한 경우예요. 원래 배리언트를 뜻했으니, rustc의 help대로 괄호를 붙여 주세요. 전체 경로 Shape::Circle(_)로 쓰면 더 분명해요.
enum Shape { Circle(f64), Square(f64),}
use Shape::*;
fn name(shape: &Shape) -> &'static str { match shape { Circle(..) => "원", // ✅ 배리언트, 안의 값은 무엇이든 Square(_) => "정사각형", }}
fn area(shape: &Shape) -> f64 { match shape { Shape::Circle(r) => 3.14 * r * r, // ✅ 전체 경로로, 값도 사용 Shape::Square(side) => side * side, }}
fn main() { let shapes = [Circle(1.0), Square(2.0)]; for shape in &shapes { println!("{}: {:.2}", name(shape), area(shape)); }}실행 결과:
원: 3.14정사각형: 4.00..는 “안에 무엇이 있든”이라는 뜻이에요. 나중에 배리언트에 값이 더 생겨도 그대로 동작해요. _와 ..로 일부 무시하기를 참고하세요.
방법 2: 변수 이름 바꾸기
섹션 제목: “방법 2: 변수 이름 바꾸기”정말로 새 변수를 원했는데, 그 이름이 우연히 타입 이름과 같을 때 이 방법을 고르세요. 변수 이름을 타입 이름 그대로 지으면, let이나 함수 매개변수에서 자주 생겨요.
error[E0530]: let bindings cannot shadow tuple structs --> src/main.rs:4:9 |1 | struct Meters(f64); | ------------------- the tuple struct `Meters` is defined here...4 | let Meters = 5.0; // ❌ `Meters`는 튜플 구조체예요 | ^^^^^^ cannot be named the same as a tuple struct라벨은 “튜플 구조체와 같은 이름을 붙일 수 없다”는 뜻이에요. 문제가 되는 프로그램은 이래요.
struct Meters(f64);
fn main() { let Meters = 5.0; // ❌ `Meters`는 튜플 구조체예요 println!("{}", Meters);}Rust의 이름 규칙이 이 문제를 대신 해결해 줘요. 타입과 배리언트는 CamelCase, 변수는 snake_case로 써요. 소문자 이름을 쓰면 충돌이 사라져요.
struct Meters(f64);
fn total(meters: &[Meters]) -> f64 { meters.iter().map(|m| m.0).sum()}
fn main() { let meters = 5.0; // ✅ 소문자 변수 이름 println!("{} m", meters);
let Meters(height) = Meters(1.8); // ✅ 또는 튜플 구조체를 구조 분해하기 println!("키: {} m", height);
println!("합계: {} m", total(&[Meters(2.0), Meters(3.5)]));}실행 결과:
5 m키: 1.8 m합계: 5.5 m매개변수도 마찬가지예요. fn show(Point: i32)는 function parameters cannot shadow tuple structs(함수 매개변수는 튜플 구조체를 가릴 수 없다)를 내고, fn show(point: i32)로 고치면 돼요. 이름 짓는 규칙을 참고하세요.
방법 3: static 대신 const(또는 매치 가드) 쓰기
섹션 제목: “방법 3: static 대신 const(또는 매치 가드) 쓰기”메시지에 statics가 있을 때 이 방법을 고르세요. 아마 정해진 값과 비교하고 싶었을 거예요. static은 패턴으로 쓸 수 없지만 const는 쓸 수 있어요.
error[E0530]: match bindings cannot shadow statics --> src/main.rs:5:9 |1 | static LIMIT: u32 = 100; | ------------------------ the static `LIMIT` is defined here...5 | LIMIT => "가득 참", // ❌ static은 패턴이 될 수 없어요 | ^^^^^ cannot be named the same as a static라벨은 “static과 같은 이름을 붙일 수 없다”는 뜻이에요. 문제가 되는 프로그램은 이래요.
static LIMIT: u32 = 100;
fn status(users: u32) -> &'static str { match users { LIMIT => "가득 참", // ❌ static은 패턴이 될 수 없어요 _ => "자리 있음", }}
fn main() { println!("{}", status(100));}static을 const로 바꾸세요. 꼭 static으로 남겨야 한다면(예를 들어 다른 코드가 그 주소를 가져다 쓴다면), 값을 이름에 묶고 매치 가드에서 비교하세요.
const LIMIT: u32 = 100; // ✅ const는 패턴으로 쓸 수 있어요static MAX_USERS: u32 = 500; // static은 그대로 static
fn status(users: u32) -> &'static str { match users { LIMIT => "가득 참", _ => "자리 있음", }}
fn premium_status(users: u32) -> &'static str { match users { n if n >= MAX_USERS => "가득 참", // ✅ static은 가드에서 비교해요 _ => "자리 있음", }}
fn main() { println!("{}", status(100)); println!("{}", status(42)); println!("{}", premium_status(500));}실행 결과:
가득 참자리 있음가득 참그냥 숫자 하나라면 static이 필요한 경우는 거의 없어요. 보통은 const를 써요. 상수를 참고하세요.
자주 보게 되는 곳
섹션 제목: “자주 보게 되는 곳”괄호 없는 Ok =>나 Some =>. Ok, Err, Some은 프렐루드에 있는 튜플 배리언트라서, match result { Ok => ... }는 try specify the pattern arguments: `Ok(..)` 라는 help와 함께 E0530을 내요. 해결: Ok(_) =>나 Ok(value) =>라고 쓰세요.
if let 안에서. if let Some(Some) = x는 안쪽 값을 Some이라고 부르려고 해서 let bindings cannot shadow tuple variants(let 바인딩은 튜플 배리언트를 가릴 수 없다)를 내요. 해결: if let Some(inner) = x처럼 소문자 이름을 쓰세요.
전체 경로로 쓰면 대신 E0532가 나요. Shape::Circle =>는 새 변수가 될 수 없어요(변수 이름에는 ::가 없으니까요). 그래서 rustc는 E0532, expected unit struct, unit variant or constant, found tuple variant를 보고해요. 해결은 같아요. (_)나 (..)를 붙이세요.
유닛 구조체 이름으로 쓴 let은 E0308이에요. struct Guest;가 있으면 let Guest = "Ana";는 변수를 아예 만들지 않아요. rustc는 Guest를 유닛 구조체로 읽고 `Guest` is interpreted as a unit struct, not a new binding(Guest는 새 바인딩이 아니라 유닛 구조체로 해석된다)라며 E0308(타입 불일치)을 보고해요. 해결: let guest = "Ana";처럼 변수 이름을 바꾸세요.
관련 에러
섹션 제목: “관련 에러”- E0532: expected unit struct, unit variant or constant, found tuple variant: 같은 실수를
Shape::Circle =>처럼 경로로 쓴 경우예요. - E0533: expected unit struct, unit variant or constant, found struct variant: 이름 있는 필드를 가진 배리언트를
{ .. }없이 썼어요. - E0004: non-exhaustive patterns: 갈래를 고친 뒤에도
match는 모든 배리언트를 다뤄야 해요. - E0308: mismatched types: 유닛 구조체 이름으로
let을 쓰면 나는 에러예요. - E0425: cannot find value in this scope: 반대 문제예요. 아예 존재하지 않는 이름이에요.
- 전체 컴파일러 에러: 에러 페이지 전체 목록이에요.
이해도 점검
섹션 제목: “이해도 점검”이해도 점검
1. Circle(f64)가 배리언트이고 use Shape::*;를 썼어요. 어떤 match 갈래가 컴파일될까요?
2. let Meters = 5.0;에 rustc가 "let bindings cannot shadow tuple structs"라고 해요. 가장 간단한 해결책은 무엇일까요?
3. static LIMIT: u32 = 100;은 match 패턴으로 쓸 수 없어요. 무엇은 쓸 수 있을까요?
점수: 0 / 3
더 알아보기
섹션 제목: “더 알아보기”- E0530 — Rust error codes index (영어):
(_)를 빠뜨린 경우와static대const경우를 다루는 공식 설명이에요. - 패턴 문법 — The Rust Programming Language 한국어판: 이름 있는 변수 매칭, 열거형 구조 분해,
_와..로 값 무시하기를 다뤄요. - Identifier patterns — The Rust Reference (영어): 패턴 속 이름이 언제 새 변수가 되고, 언제 이미 있는 것을 가리키는지에 대한 정확한 규칙이에요.
- Destructuring enums — Rust by Example (영어): 유닛 배리언트와 튜플 배리언트의 매칭을 나란히 보여 줘요.
- Static items — The Rust Reference (영어):
static이const와 어떻게 다른지 설명해요(쓰이는 곳마다 복사되는 값이 아니라, 메모리 속 고정된 한 곳이에요).