Rust 에러 E0610: primitive type doesn't have fields (숫자에는 필드가 없음)
E0610은 something.field라고 썼는데, something이 평범한 숫자(또는 bool이나 char)라서 필드가 아예 없다는 뜻이에요. 거의 항상 변수가 생각한 것과 다른 타입이라는 신호예요. 이미 구조체에서 숫자를 꺼냈을 수도 있고, 함수가 구조체 대신 숫자를 반환했을 수도 있고, .0을 하나 더 썼을 수도 있어요. 메시지가 Rust가 보고 있는 타입을 정확히 알려 주니까, 해결까지 한 걸음이에요. 이 시리즈의 다른 에러는 Rust 컴파일러 에러 해설에서 볼 수 있어요.
에러 메시지
섹션 제목: “에러 메시지”error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields --> src/main.rs:3:22 |3 | println!("{}", x.value); // ❌ x는 숫자라서 `value` 필드가 없어요 | ^^^^^“{integer} is a primitive type and therefore doesn’t have fields”는 “{integer}는 기본 타입(primitive type)이라서 필드가 없다”는 뜻이에요.
이 에러를 내는 프로그램은 다음과 같아요.
fn main() { let x = 5; println!("{}", x.value); // ❌ x는 숫자라서 `value` 필드가 없어요}무슨 뜻인가요
섹션 제목: “무슨 뜻인가요”백틱 안의 타입은 Rust가 생각하는 변수의 타입이에요. 여기서는 `{integer}`인데, “어떤 정수인데, 정확히 어떤 종류인지는 아직 정하지 않았다”는 뜻이에요(다른 단서가 없으면 Rust가 나중에 i32로 정해요). 소수라면 `{float}`가 나오고, u32, usize, f64, bool, char 같은 구체적인 타입이 나올 수도 있어요.
^^^^^는 점 뒤의 이름을 가리켜요. Rust는 “value의 철자가 틀렸다”고 말하는 게 아니에요. “숫자에는 이름이 무엇이든 어떤 필드도 없다”고 말하는 거예요.
그러니 스스로 물어야 할 질문은 이거예요. 이 변수는 왜 숫자일까요? “5의 가격표”를 달라고 하는 것과 비슷해요. 상품에는 가격표가 있지만, 숫자 5는 그냥 숫자예요.
Rust가 이렇게 하는 이유
섹션 제목: “Rust가 이렇게 하는 이유”i32, u32, f64, bool, char 같은 기본 타입은 언어에 내장된 단일 값이에요. 구조체가 아니니까, 점과 이름으로 닿을 수 있는 게 안에 아무것도 없어요. 데이터 타입을 참고하세요.
그래도 메서드는 있어요. x.abs(), x.pow(2), price.round(), c.is_alphabetic()처럼요. 메서드는 ()를 붙여 호출해요. 필드는 구조체 안에 저장된 데이터이고, 필드를 가진 건 구조체, 튜플, 유니온뿐이에요.
Rust는 모든 .field를 컴파일할 때 검사하기 때문에, 이런 착각이 실행 중의 크래시나 조용한 undefined가 될 수 없어요. 정확히 그 줄에서 바로 알게 돼요.
고치는 방법
섹션 제목: “고치는 방법”방법 1: 숫자를 그대로 쓰기
섹션 제목: “방법 1: 숫자를 그대로 쓰기”앞에서 이미 구조체에서 필드를 꺼냈을 때 이 방법을 고르세요. let count = counter.value; 다음에는 count가 곧 숫자라서, count.value는 u32의 필드를 달라는 말이 돼요.
error[E0610]: `u32` is a primitive type and therefore doesn't have fields --> src/main.rs:8:26 |8 | println!("{}", count.value); | ^^^^^숫자를 있는 그대로 쓰거나, 구조체로 돌아가서 거기서 필드를 읽으세요.
struct Counter { value: u32,}
fn main() { let mut counter = Counter { value: 0 }; counter.value += 3;
let count = counter.value; // 이제 count는 평범한 u32예요 println!("{}", count); // ✅ 숫자 자체를 써요 println!("{}", counter.value); // ✅ 또는 구조체에서 필드를 읽어요}실행 결과:
33변수의 타입을 모르겠다면 에디터에서 변수 위에 마우스를 올려 보세요(rust-analyzer가 필요해요. Rust 도구를 참고하세요). 아니면 잠깐 let _: () = count;라고 써 보세요. rustc가 expected `()`, found `usize`(()를 기대했는데 usize가 왔다)라고 답하면서 진짜 타입을 알려 줘요. 확인한 뒤에는 그 줄을 지우세요.
방법 2: 숫자가 아니라 구조체를 반환하거나 저장하기
섹션 제목: “방법 2: 숫자가 아니라 구조체를 반환하거나 저장하기”값이 함수나 컬렉션에서 왔고, 구조체일 거라고 기대했을 때 이 방법을 고르세요. 흔한 경우가 두 가지 있어요.
fn find_order() -> u32같은 함수가 주문 id를 반환하는데,order가u32라서order.total이 실패해요.HashMap<&str, u32>에 점수만 저장했는데, 각 값이Player가 아니라u32라서scores["ana"].score가 실패해요.
정말 원하는 게 무엇인지 정하세요. 데이터가 여러 개 필요하다면, 반환 타입이나 값의 타입을 구조체로 바꾸세요.
use std::collections::HashMap;
struct Order { id: u32, total: u32, // 센트 단위}
// 숫자 하나가 아니라 Order 전체를 반환해요fn new_order(id: u32, total: u32) -> Order { Order { id, total }}
struct Player { score: u32, level: u8,}
fn main() { let order = new_order(7, 1999); println!("주문 {}의 가격: {}센트", order.id, order.total);
// 맵에 점수만이 아니라 Player 전체를 저장해요 let mut players: HashMap<&str, Player> = HashMap::new(); players.insert("ana", Player { score: 10, level: 2 });
let ana = &players["ana"]; println!("ana: 점수 {}, 레벨 {}", ana.score, ana.level);}실행 결과:
주문 7의 가격: 1999센트ana: 점수 10, 레벨 2숫자 하나만 있으면 된다면, u32는 그대로 두고 .field 부분만 지우세요. 구조체와 해시맵을 참고하세요.
방법 3: 남는 .0 지우기
섹션 제목: “방법 3: 남는 .0 지우기”에러가 .0이나 .1을 가리킬 때 이 방법을 고르세요. 튜플과 튜플 구조체는 필드에 위치 번호를 쓰는데, 한 단계 더 깊이 들어가기 쉬워요. let x = point.0; 다음에는 x가 이미 숫자라서, x.0은 `{integer}` is a primitive type을 내요. struct Meters(u32); 같은 감싸는 타입도 마찬가지예요. .0 하나로 u32를 꺼내고, 두 번째 .0은 실패해요.
struct Meters(u32);
fn main() { let point = (3, 4); let x = point.0; // x는 이미 숫자 3이에요 println!("x = {}", x); // x.0이 아니에요
let m = Meters(42); println!("{} m", m.0); // .0 하나로 튜플 구조체에서 u32를 꺼내요
let line = ((0, 0), (3, 4)); // 중첩된 튜플: 한 단계마다 위치 하나 println!("끝점 y = {}", line.1.1);}실행 결과:
x = 342 m끝점 y = 4단계를 세어 보세요. .0이나 .1 하나가 튜플 안으로 한 단계 들어가요. 숫자에 닿으면 멈추세요. 위치 번호가 헷갈린다면, 대신 구조체로 이름을 붙이세요. 위치로 요소에 접근하기와 튜플과 구조체, 언제 무엇을 쓸까요를 참고하세요.
자주 보게 되는 곳
섹션 제목: “자주 보게 되는 곳”() 없이 쓴 메서드. f64에 price.round나 i32에 x.abs라고 쓰면 E0610이 아니라 E0615, “attempted to take value of method”(메서드의 값을 가져가려고 했다)가 나요. 그 이름들은 정말로 메서드이기 때문이에요. 해결: price.round(), x.abs(). 한 가지 놀라운 점이 있어요. 타입이 아직 {integer}라면(let x = -5;처럼), x.abs도 E0610을 내고, x.abs()는 E0689 “can’t call method abs on ambiguous numeric type {integer}”(모호한 숫자 타입 {integer}에는 abs 메서드를 호출할 수 없다)를 내요. let x: i32 = -5;처럼 타입을 정하면 x.abs()가 동작해요.
숫자의 참조자. 변수가 u32가 아니라 &u32라면 코드가 E0609 에러로 바뀌어요. no field `value` on type `&u32`예요. 흔한 곳은 for (name, score) in &players 같은 반복문이에요. 여기서 score는 &i32라서, score.1은 no field `1` on type `&{integer}`를 내요. 원인도 같고 고치는 법도 같아요. score가 이미 그 숫자예요.
len()이나 count()의 결과. let len = scores.len();은 usize를 주니까, len.count는 `usize` is a primitive type으로 실패해요. 해결: len을 그대로 쓰세요.
단위가 있는 숫자. price.cents나 distance.meters처럼 쓰고 싶다면, 작은 구조체를 만드세요. struct Price { cents: u64 }처럼요. 그러면 price.cents가 동작하고, 가격과 다른 숫자를 헷갈리는 것도 타입이 막아 줘요. 구조체를 참고하세요.
관련 에러
섹션 제목: “관련 에러”- E0609: no field
xon typeT: 그 타입에 그런 이름의 필드가 없어요.&u32같은 참조자에서는 E0610이 아니라 이 에러가 나요. - E0615: attempted to take value of method:
price.round()대신price.round라고 썼어요. E0689: can't call method on ambiguous numeric type: 아직 타입이 정해지지 않은 숫자에 메서드를 호출했어요.i32같은 타입을 붙이세요.- E0599: no method named x found: 메서드 버전이에요. 예를 들어 숫자에 없는 메서드를 호출했을 때예요.
- E0308: mismatched types: 함수가 숫자를 반환하는데 구조체를 기대했을 때 E0610과 함께 자주 나와요.
- 전체 컴파일러 에러: 에러 페이지 전체 목록이에요.
이해도 점검
섹션 제목: “이해도 점검”이해도 점검
1. let total = cart.total;(total은 u32 필드) 다음에 total.amount라고 쓰면 어떻게 될까요?
2. 메시지 속 {integer}는 무슨 뜻일까요?
3. let x = point.0; 다음에 x.0이라고 쓰면 E0610이 나요. 어떻게 고칠까요?
점수: 0 / 3
더 알아보기
섹션 제목: “더 알아보기”- E0610 — Rust error codes index (영어): 이 에러의 공식 설명이에요.
- 데이터 타입 — The Rust Programming Language 한국어판: 스칼라 타입(정수, 부동소수점,
bool,char)과 복합 타입(튜플, 배열)을 설명해요. - 명명된 필드 없는 튜플 구조체 — The Rust Programming Language 한국어판:
Meters(u32)같은 감싸는 타입과, 거기서.0이 어떻게 동작하는지 알려 줘요. - Primitives — Rust by Example (영어): 내장 타입들을 실행 예제와 함께 보여 줘요.
i32— 표준 라이브러리 문서 (영어): 정수가 가진 모든 메서드예요(전부()를 붙여 호출한다는 걸 볼 수 있어요).