콘텐츠로 이동

Rust 에러 E0560: struct has no field named X (구조체에 없는 필드 이름)

E0560은 Name { field: value }로 구조체를 만드는데, 적어 넣은 필드 중 하나가 그 구조체에 없다는 뜻이에요. 보통은 오타이거나, 이름이 바뀐 필드이거나, 다른 구조체의 필드예요. rustc가 원래 쓰려던 이름을 정확히 알려 줄 때가 많아서, 가장 빨리 고칠 수 있는 에러 중 하나예요. 이 시리즈의 다른 에러는 Rust 컴파일러 에러 해설에서 볼 수 있어요.

error[E0560]: struct `User` has no field named `nmae`
--> src/main.rs:9:9
|
9 | nmae: String::from("Ferris"), // ❌ 오타: 필드 이름은 `name`이에요
| ^^^^ unknown field
|
help: a field with a similar name exists
|
9 - nmae: String::from("Ferris"), // ❌ 오타: 필드 이름은 `name`이에요
9 + name: String::from("Ferris"), // ❌ 오타: 필드 이름은 `name`이에요
|

“struct User has no field named nmae“는 “User 구조체에는 nmae라는 이름의 필드가 없다”는 뜻이에요. 라벨 unknown field는 “알 수 없는 필드”, help는 “비슷한 이름의 필드가 있다”는 뜻이고, 바꿀 줄을 그대로 보여 줘요.

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

struct User {
name: String,
email: String,
active: bool,
}
fn main() {
let user = User {
nmae: String::from("Ferris"), // ❌ 오타: 필드 이름은 `name`이에요
email: String::from("[email protected]"),
active: true,
};
println!("{}", user.email);
}

첫 줄은 구조체(User)와 내가 쓴 필드(nmae)를 알려 줘요. ^^^^는 User { ... } 블록 안의 그 필드를 가리켜요. Rust가 그 이름을 struct User의 필드 목록과 비교해 봤는데, 맞는 게 없었던 거예요.

화살표 아래에는 세 가지 힌트 중 하나가 나와요. 각각 알려 주는 게 달라요.

  • help: a field with a similar name exists(비슷한 이름의 필드가 있다): 오타예요. rustc가 올바른 이름으로 고친 줄을 보여 줘요.
  • note: available fields are: `username`(쓸 수 있는 필드는 username): 비슷한 이름이 없어요. 아직 채우지 않은 필드들이 나열돼요.
  • note: all struct fields are already assigned(구조체의 모든 필드에 이미 값을 넣었다): 진짜 필드는 전부 채웠고, 이 필드는 남는 거예요.

rustc가 name이 빠졌다고 따로 불평하지 않는다는 점도 눈여겨보세요. 오타를 고치면 두 문제가 한꺼번에 사라져요.

종이 신청서를 채우면서 전화번호를 “팩스” 칸에 적은 것과 비슷해요. 신청서에 그런 칸이 없으니, 직원이 다시 돌려주는 거죠.

구조체의 필드는 struct User { ... }를 쓸 때 정해져요. User를 만들 때마다 Rust는 정확히 그 필드들을 넣었는지 검사해요. 빠진 것도, 남는 것도, 철자가 틀린 것도 없어야 해요. 구조체 정의하고 만들기를 참고하세요.

이 검사는 진짜 버그를 잡아 줘요. JavaScript에서 { nmae: "Ferris" }라고 쓰면 아무 말 없이 키가 잘못된 객체가 만들어지고, 한참 뒤 어딘가에서 user.name이 undefined가 돼요. Rust에서는 오타가 컴파일러를 통과할 수 없어서, 프로그램 안의 모든 User에 정말로 name이 있어요.

a field with a similar name exists가 보일 때 이 방법을 고르세요. help: 줄의 이름을 쓰면 돼요. 필드 이름은 대소문자를 구별하고, Rust는 snake_case를 쓰니까 userName과 user_name은 서로 다른 이름이에요.

필드 초기화 축약법(field init shorthand)도 조심하세요. User { nmae, email }은 User { nmae: nmae, email: email }이라는 뜻이라서, 변수 이름이 필드 이름과 같아야 해요. 변수 이름을 바꾸거나, 필드를 풀어서 쓰세요. User { name: nmae, email }처럼요.

struct User {
name: String,
email: String,
active: bool,
}
fn main() {
let user = User {
name: String::from("Ferris"), // ✅ 진짜 필드 이름이에요
email: String::from("[email protected]"),
active: true,
};
println!("{} ({}) 활성: {}", user.name, user.email, user.active);
// 축약법: 변수 이름이 필드 이름과 같아요
let name = String::from("Ada");
let email = String::from("[email protected]");
let other = User { name, email, active: false };
println!("{} ({}) 활성: {}", other.name, other.email, other.active);
}

실행 결과:

Ferris ([email protected]) 활성: true
Ada ([email protected]) 활성: false
▶ 직접 실행해 보기

방법 2: 필드의 새 이름을 쓰거나, 필드 추가하기

섹션 제목: “방법 2: 필드의 새 이름을 쓰거나, 필드 추가하기”

“비슷한 이름” 힌트가 없을 때 이 방법을 고르세요. 구조체가 바뀐 경우가 많아요. 누군가 name을 username으로 바꿨거나 필드를 지웠는데, 이 코드는 아직 옛날 이름을 쓰는 거예요. rustc는 남아 있는 필드를 알려 줘요.

error[E0560]: struct `User` has no field named `name`
--> src/main.rs:8:9
|
8 | name: String::from("Ferris"),
| ^^^^ `User` does not have this field
|
= note: available fields are: `username`

라벨 `User` does not have this field는 “User에는 이 필드가 없다”는 뜻이에요.

선택지는 두 가지예요. 구조체가 맞다면, 지금의 필드 이름을 쓰세요. 내 코드가 맞고 구조체에 정말 그 필드가 필요하다면, 구조체 정의에 필드를 추가하세요. 아래에서 Config는 debug에 대해 all struct fields are already assigned를 냈고, 해결책은 구조체에 debug를 추가하는 거였어요.

struct User {
username: String, // `name`에서 이름이 바뀌었어요: 새 이름을 써요
email: String,
}
struct Config {
port: u16,
host: String,
debug: bool, // 코드에 필요해서 구조체에 추가했어요
}
fn main() {
let user = User {
username: String::from("ferris"),
email: String::from("[email protected]"),
};
println!("{} ({})", user.username, user.email);
let config = Config {
port: 8080,
host: String::from("localhost"),
debug: true,
};
println!("{}:{} debug={}", config.host, config.port, config.debug);
}

실행 결과:

localhost:8080 debug=true
▶ 직접 실행해 보기

방금 업데이트한 크레이트의 구조체라면, 새 버전에서 필드 이름이 바뀌었을 수도 있어요. 그 버전의 구조체를 docs.rs(영어)에서 확인하거나, 크레이트의 변경 기록(changelog)을 보세요. 직접 만든 구조체에 필드를 추가하면, 그 구조체를 만드는 모든 곳에서 그 필드도 채워야 해요. rustc가 그런 곳마다 E0063으로 짚어 줄 거예요(아래 참고).

내가 쓴 필드가 있긴 한데, 다른 구조체에 있을 때 이 방법을 고르세요. title이 있는 Product와 name이 있는 Customer처럼 비슷한 구조체가 두 개 있으면 헷갈리기 쉬워요.

error[E0560]: struct `Product` has no field named `name`
--> src/main.rs:13:9
|
13 | name: String::from("Keyboard"),
| ^^^^ `Product` does not have this field
|
= note: available fields are: `title`

available fields note를 보세요. 예상하지 못한 필드가 나열돼 있다면, 엉뚱한 구조체를 만들고 있는 거예요. 맞는 구조체 이름을 쓰거나, 원래 쓰려던 구조체의 필드 이름을 쓰세요.

struct Product {
title: String,
price: u32, // 센트 단위
}
struct Customer {
name: String,
email: String,
}
fn main() {
let keyboard = Product {
title: String::from("Keyboard"), // 상품에는 `title`이 있어요
price: 4900,
};
let buyer = Customer {
name: String::from("Ana"), // 고객에게는 `name`이 있어요
email: String::from("[email protected]"),
};
println!("{} 님이 {}를 {}센트에 사요", buyer.name, keyboard.title, keyboard.price);
println!("영수증을 보낸 곳: {}", buyer.email);
}

실행 결과:

Ana 님이 Keyboard를 4900센트에 사요
영수증을 보낸 곳: [email protected]
▶ 직접 실행해 보기

방법 4: 튜플 구조체에는 괄호 쓰기

섹션 제목: “방법 4: 튜플 구조체에는 괄호 쓰기”

구조체가 struct Point(i32, i32); 같은 튜플 구조체일 때 이 방법을 고르세요. 튜플 구조체의 필드에는 이름이 없어서, Point { x: 1, y: 2 }라고 쓰면 x와 y 둘 다 E0560이 나요. 이때 help: `Point` is a tuple struct, use the appropriate syntax(Point는 튜플 구조체이니 알맞은 문법을 쓰라)와 함께 Point(/* i32 */, /* i32 */)라는 제안이 붙어요. Point(1, 2)처럼 괄호로 만들고, .0과 .1로 읽으세요. 이름을 붙이고 싶다면 정의를 struct Point { x: i32, y: i32 }로 바꾸세요. 튜플과 구조체, 언제 무엇을 쓸까요를 참고하세요.

E0560의 쌍둥이는 E0063이에요. 구조체에 있는 필드인데 내가 채우지 않은 경우예요. Rust는 필드를 대신 채워 주지 않으니, 모든 필드에 값이 있어야 해요.

error[E0063]: missing field `active` in initializer of `User`
--> src/main.rs:8:16
|
8 | let user = User {
| ^^^^ missing `active`

“missing field active in initializer of User“는 “User를 초기화하는 곳에 active 필드가 빠졌다”는 뜻이에요. 여러 필드가 빠졌다면 전부 나열해 줘요. missing fields `active`, `email` and `login_count` in initializer of `User`처럼요. 해결: 모든 필드에 값을 주세요. 많은 필드에 적당한 기본값이 있다면, Default를 derive하고 구조체 업데이트 문법 ..으로 나머지를 채우세요.

#[derive(Debug, Default)]
struct Settings {
volume: u8,
dark_mode: bool,
language: String,
}
fn main() {
// 모든 필드를 다 써요
let full = Settings { volume: 80, dark_mode: false, language: String::from("en") };
// 신경 쓰는 필드만 쓰고, 나머지는 Default에서 가져와요
let quick = Settings { dark_mode: true, ..Default::default() };
// 나머지를 다른 값에서 복사해요
let louder = Settings { volume: 100, ..full };
println!("{:?}", quick);
println!("{:?}", louder);
}

실행 결과:

Settings { volume: 0, dark_mode: true, language: "" }
Settings { volume: 100, dark_mode: false, language: "en" }
▶ 직접 실행해 보기

숫자의 기본값은 0, bool은 false, String은 빈 문자열이에요. 다른 기본값을 원한다면 impl Default for Settings를 직접 쓰세요. Default: 적당한 시작 값을 참고하세요.

.. 업데이트 문법 안에서. Settings { dark: true, ..defaults }도 E0560을 내요. 이때 note는 모든 필드를 나열하고(available fields are: `volume`, `dark_mode`, `language`), 이름이 너무 다르면 “비슷한 이름” 힌트가 없을 수도 있어요. 해결: dark_mode: true.

이름 있는 필드를 가진 열거형 배리언트. enum Shape { Circle { radius: f64 } }에서 Shape::Circle { r: 2.0 }이라고 쓰는 건 같은 실수지만, rustc는 다른 코드를 써요. E0559, variant `Shape::Circle` has no field named `r`(Shape::Circle 배리언트에는 r이라는 필드가 없다)이고, 같은 available fields note와 오타 힌트가 붙어요. 해결: Shape::Circle { radius: 2.0 }. 열거형은 데이터를 담을 수 있어요를 참고하세요.

다른 모듈의 구조체. 필드는 있지만 pub이 아니라면, 대신 E0451, “field balance of struct Account is private”(Account 구조체의 balance 필드는 비공개다)이 나요. 해결: Account::new(...)처럼 모듈이 제공하는 생성자를 쓰세요. 기본은 모두 비공개를 참고하세요.

이해도 점검

1. rustc가 note: all struct fields are already assigned라고 해요. 무슨 뜻일까요?

2. let nmae = ...;가 있는데 User { nmae, email }이라고 썼어요. 어떻게 고칠까요?

3. active 없이 User { name, email }처럼 필드를 빠뜨리면 어떤 에러가 날까요?

점수: 0 / 3