콘텐츠로 이동

Rust 에러 E0533: expected unit struct, unit variant or constant, found struct variant

E0533은 Shape::Circle 같은 이름을 그것만 덩그러니 썼는데, 그 자리에서는 이름만으로 부족하다는 뜻이에요. 가장 흔한 경우는 이름 있는 필드를 가진 열거형 배리언트(Circle { radius: f64 })를 { } 없이 쓴 거예요. match 갈래에서도, 값을 만들 때도 그럴 수 있어요. 드물게는 고정된 값만 올 수 있는 자리에 함수나 메서드를 쓴 경우예요. rustc가 무엇을 요구하는지 알면 둘 다 금방 고칠 수 있어요. 이 페이지는 Rust 컴파일러 에러 해설의 일부예요.

error[E0533]: expected unit struct, unit variant or constant, found struct variant `Shape::Circle`
--> src/main.rs:8:9
|
8 | Shape::Circle => "circle", // ❌ `Circle`에는 필드가 있어요
| ^^^^^^^^^^^^^ not a unit struct, unit variant or constant
|
help: the struct variant's field is being ignored
|
8 | Shape::Circle { radius: _ } => "circle", // ❌ `Circle`에는 필드가 있어요
| +++++++++++++

“expected unit struct, unit variant or constant, found struct variant Shape::Circle“은 “유닛 구조체, 유닛 배리언트, 상수 중 하나가 와야 하는데 구조체 배리언트 Shape::Circle이 왔다”는 뜻이에요. help:는 “구조체 배리언트의 필드를 무시하는 중이다”라며 필드를 무시하는 패턴을 제안해요.

이 에러를 내는 프로그램은 다음과 같아요.

enum Shape {
Circle { radius: f64 },
Square { side: f64 },
}
fn name(shape: &Shape) -> &'static str {
match shape {
Shape::Circle => "circle", // ❌ `Circle`에는 필드가 있어요
Shape::Square { .. } => "square",
}
}
fn main() {
println!("{}", name(&Shape::Square { side: 2.0 }));
}

^^^는 Shape::Circle을 가리키고, 첫 줄은 그 자리에 무엇이 올 수 있었는지 알려 줘요. 이름만 있는 패턴은 다음 셋 중 하나여야 해요.

  • 유닛 구조체(unit struct): struct Empty; 같은 것
  • 유닛 배리언트(unit variant): Shape::Empty나 None처럼 데이터가 없는 배리언트
  • 상수(constant): const MAX: u32 = 100; 같은 것

Shape::Circle은 그 어느 것도 아니에요. radius를 담고 있는 구조체 배리언트(struct variant)예요. 그냥 Shape::Circle이라고만 쓰면 “원”과 매칭되긴 하겠지만 필드에 대해서는 아무 말도 하지 않아요. Rust는 패턴의 모양이 배리언트의 모양과 맞기를 원해요. help: 줄이 해결책을 보여 줘요. 중괄호를 붙이세요.

E0533의 두 번째 종류는 expected value, found struct variant(값이 와야 하는데 구조체 배리언트가 왔다)라고 나와요. 같은 혼동이지만 값을 만들 때 생기고, 보통 Shape::Circle(2.0)처럼 괄호로 썼을 때예요.

택배 신청서에 “크기: S, M, L”이라고 적힌 칸이 있는데 “직접 입력”이라고 쓴 것과 같아요. 직접 입력도 있는 선택지이지만, 옆에 치수를 채워 넣어야 해요.

열거형 배리언트에는 세 가지 모양이 있고, 만들 때도 매칭할 때도 모양마다 쓰는 방법이 정해져 있어요(열거형은 데이터를 담을 수 있어요 참고).

배리언트 만들기 매칭하기
유닛: Empty Shape::Empty Shape::Empty
튜플: Point(f64, f64) Shape::Point(1.0, 2.0) Shape::Point(x, y) 또는 Shape::Point(..)
구조체: Circle { radius: f64 } Shape::Circle { radius: 2.0 } Shape::Circle { radius } 또는 Shape::Circle { .. }

모양을 엄격하게 지키면 패턴은 언제나 매칭하는 대상처럼 생겨요. 그래서 match를 읽기만 해도 각 갈래가 어떤 데이터를 갖는지 정확히 알 수 있어요. 또 배리언트의 모양이 바뀌면, 예를 들어 Empty에 필드가 생기면, 예전 모양을 쓰는 곳마다 표시돼요. 아무 말 없이 엉뚱하게 동작하는 대신에요.

방법 1: 패턴에 { .. }나 필드 붙이기

섹션 제목: “방법 1: 패턴에 { .. }나 필드 붙이기”

에러가 match, if let, matches!의 갈래를 가리키고 found struct variant라고 할 때 이 방법을 고르세요. 필드가 필요 없으면 { .. }를, 필요하면 필드 이름을 쓰세요. rustc가 제안한 { radius: _ }도 동작하는데, { .. }가 같은 말을 더 짧게 하는 방법이에요.

enum Shape {
Circle { radius: f64 },
Square { side: f64 },
Empty,
}
fn name(shape: &Shape) -> &'static str {
match shape {
Shape::Circle { .. } => "원", // ✅ "반지름이 얼마든 원"
Shape::Square { .. } => "정사각형",
Shape::Empty => "없음", // 유닛 배리언트: 중괄호가 필요 없어요
}
}
fn area(shape: &Shape) -> f64 {
match shape {
Shape::Circle { radius } => 3.14 * radius * radius, // ✅ 필요하니까 필드 이름을 적어요
Shape::Square { side } => side * side,
Shape::Empty => 0.0,
}
}
fn main() {
let shapes = [Shape::Circle { radius: 1.0 }, Shape::Square { side: 3.0 }, Shape::Empty];
for shape in &shapes {
println!("{}: {:.2}", name(shape), area(shape));
}
let circles = shapes.iter().filter(|s| matches!(s, Shape::Circle { .. })).count();
println!("원 {}개", circles);
}

실행 결과:

원: 3.14
정사각형: 9.00
없음: 0.00
원 1개
▶ 직접 실행해 보기

{ .. }는 필드가 몇 개든 모든 구조체 배리언트에 쓸 수 있어요. 튜플 배리언트에서는 같은 생각을 (..)로 써요. _와 ..로 일부 무시하기를 참고하세요.

방법 2: 중괄호와 필드 이름으로 값 만들기

섹션 제목: “방법 2: 중괄호와 필드 이름으로 값 만들기”

메시지가 expected value, found struct variant라고 할 때 이 방법을 고르세요. 구조체 배리언트를 튜플 배리언트처럼(또는 필드 없이) 만들고 있는 거예요.

error[E0533]: expected value, found struct variant `Shape::Circle`
--> src/main.rs:8:17
|
8 | let shape = Shape::Circle(2.0); // ❌ `Circle`에는 이름 있는 필드가 있어요
| ^^^^^^^^^^^^^ not a value
|
help: you might have meant to create a new value of the struct
|
8 - let shape = Shape::Circle(2.0); // ❌ `Circle`에는 이름 있는 필드가 있어요
8 + let shape = Shape::Circle { radius: /* value */ }; // ❌ `Circle`에는 이름 있는 필드가 있어요
|

라벨 “not a value”는 “값이 아니다”라는 뜻이고, help:는 “구조체의 새 값을 만들려던 것 같다”며 중괄호를 쓴 줄을 보여 줘요.

이 에러를 내는 프로그램이에요.

#[derive(Debug)]
enum Shape {
Circle { radius: f64 },
Square { side: f64 },
}
fn main() {
let shape = Shape::Circle(2.0); // ❌ `Circle`에는 이름 있는 필드가 있어요
println!("{:?}", shape);
}

제안에 있는 /* value */는 값을 넣을 자리 표시자예요. 구조체 배리언트의 필드는 중괄호 안에서 이름으로 정해요.

#[derive(Debug)]
enum Shape {
Circle { radius: f64 },
Square { side: f64 },
}
impl Shape {
// `Shape::circle(2.0)`이라고 쓰고 싶다면 짧은 생성자를 만들어요
fn circle(radius: f64) -> Shape {
Shape::Circle { radius }
}
}
fn main() {
let a = Shape::Circle { radius: 2.0 }; // ✅ 중괄호와 필드 이름
let b = Shape::Square { side: 1.5 };
let c = Shape::circle(0.5);
println!("{:?}", a);
println!("{:?}", b);
println!("{:?}", c);
}

실행 결과:

Circle { radius: 2.0 }
Square { side: 1.5 }
Circle { radius: 0.5 }
▶ 직접 실행해 보기

Shape::Circle(2.0)이라고 쓰는 게 더 좋다면 정의를 튜플 배리언트 Circle(f64)로 바꾸세요. 필드가 하나뿐이고 뜻이 뻔할 때는 좋은 선택이에요. 필드가 여러 개라면 이름 있는 필드가 더 알아보기 쉬워요.

방법 3: 함수 대신 상수나 매치 가드 쓰기

섹션 제목: “방법 3: 함수 대신 상수나 매치 가드 쓰기”

메시지에 found associated function(연관 함수가 왔다)이라고 나올 때 이 방법을 고르세요. String::len 같은 메서드에서도 나요. 패턴은 컴파일할 때 알 수 있는 값이어야 해요. 함수 호출은 그렇지 않으니, 함수 이름은 패턴으로 쓸 수 없어요.

error[E0533]: expected unit struct, unit variant or constant, found associated function `Limits::max_users`
--> src/main.rs:12:9
|
12 | Limits::max_users => "full", // ❌ 함수는 패턴이 될 수 없어요
| ^^^^^^^^^^^^^^^^^ not a unit struct, unit variant or constant

이 에러를 내는 프로그램이에요.

struct Limits;
impl Limits {
fn max_users() -> u32 {
100
}
}
fn status(users: u32) -> &'static str {
match users {
0 => "empty",
Limits::max_users => "full", // ❌ 함수는 패턴이 될 수 없어요
_ => "open",
}
}
fn main() {
println!("{}", status(100));
}

값이 절대 바뀌지 않는다면 const로 만드세요. 상수는 패턴이 될 수 있어요. 계산이 필요한 값이라면 함수는 그대로 두고, if를 쓴 매치 가드(match guard)에서 비교하세요.

struct Limits;
impl Limits {
const MAX_USERS: u32 = 100; // 상수는 패턴으로 쓸 수 있어요
fn max_rooms(premium: bool) -> u32 {
if premium { 20 } else { 5 }
}
}
fn status(users: u32) -> &'static str {
match users {
0 => "비어 있음",
Limits::MAX_USERS => "꽉 참", // ✅ 상수
_ => "자리 있음",
}
}
fn rooms_status(rooms: u32, premium: bool) -> &'static str {
match rooms {
n if n >= Limits::max_rooms(premium) => "더는 방을 만들 수 없어요", // ✅ 가드 안에서 호출해요
_ => "방을 하나 더 만들 수 있어요",
}
}
fn main() {
println!("{}", status(0));
println!("{}", status(100));
println!("{}", status(42));
println!("{}", rooms_status(5, false));
println!("{}", rooms_status(5, true));
}

실행 결과:

비어 있음
꽉 참
자리 있음
더는 방을 만들 수 없어요
방을 하나 더 만들 수 있어요
▶ 직접 실행해 보기

매치 가드는 매치 가드: if 덧붙이기에서 설명해요.

유닛 배리언트에 필드가 생긴 뒤. Status::Done은 원래 유닛 배리언트였는데, Done { at: u64 }로 바꿨어요. 이제 Status::Done => 갈래와 let s = Status::Done;마다 E0533이 나요. 해결: 패턴에서는 Status::Done { .. } =>, 값을 만드는 곳에서는 (진짜 값을 넣어) Status::Done { at: 0 }이라고 쓰세요.

다른 언어에서 넘어왔을 때. Java나 TypeScript에서는 값의 종류만 확인하려고 case Circle:이라고 쓸 수도 있어요. Rust에서는 Shape::Circle { .. } =>라고 쓰거나, bool이 바로 필요하면 matches!(shape, Shape::Circle { .. })라고 쓰세요.

튜플 배리언트 버전. Circle(f64) 같은 튜플 배리언트에 그냥 Shape::Circle => 갈래를 쓰면 다른 코드인 E0532가 나요. 첫 줄은 같은 모양으로 “expected unit struct, unit variant or constant, found tuple variant Shape::Circle“이에요. 해결은 Shape::Circle(_)이나 Shape::Circle(..)이에요. 반대로 Circle { radius: f64 }에 대해 Shape::Circle(r) =>처럼 구조체 배리언트를 패턴에서 괄호로 쓰면 E0164, “expected tuple struct or tuple variant, found struct variant”(튜플 구조체나 튜플 배리언트가 와야 하는데 구조체 배리언트가 왔다)가 나요. 해결: Shape::Circle { radius: r } =>.

이해도 점검

1. enum Shape { Circle { radius: f64 } }에서 컴파일되는 match 갈래는 무엇일까요?

2. let s = Shape::Circle(2.0);에서 "expected value, found struct variant"가 나요. 어떻게 고칠까요?

3. 다음 중 그것만으로 match 패턴이 될 수 있는 것은 무엇일까요?

점수: 0 / 3