Rust 에러 E0616: field of struct is private (비공개 필드)
E0616은 찾는 필드가 있긴 하지만, 그 구조체가 정의된 모듈 안에서만 쓸 수 있는 비공개(private) 필드라는 뜻이에요. 구조체를 갖고 있을 수는 있지만, 그 안을 들여다볼 수는 없는 거예요. 구조체를 처음으로 따로 모듈에 옮긴 사람, 그리고 v.len() 대신 v.len이라고 쓴 사람이라면 누구나 만나는 에러예요. 해결책은 보통 메서드 호출이에요. 이 페이지는 Rust 컴파일러 에러 해설의 일부예요.
에러 메시지
섹션 제목: “에러 메시지”error[E0616]: field `balance` of struct `Account` is private --> src/main.rs:16:50 |16 | println!("{} has {}", account.owner, account.balance); // ❌ 비공개 필드 | ^^^^^^^ private field“구조체 Account의 필드 balance는 비공개”라는 뜻이에요. 라벨 private field는 “비공개 필드”예요.
이 에러를 내는 프로그램은 다음과 같아요.
mod bank { pub struct Account { pub owner: String, balance: u64, // 비공개 }
impl Account { pub fn new(owner: &str) -> Account { Account { owner: owner.to_string(), balance: 0 } } }}
fn main() { let account = bank::Account::new("Ferris"); println!("{} has {}", account.owner, account.balance); // ❌ 비공개 필드}같은 에러가 몇 가지 모양으로 나타나요.
| 내가 쓴 코드 | rustc가 하는 말 | 해결 |
|---|---|---|
account.balance |
field `balance` of struct `Account` is private |
방법 2 또는 방법 3 |
Vec에 scores.len |
field `len` of struct `Vec` is private, 그리고 help: a method `len` also exists, call it with parentheses |
방법 1 |
Duration에 timeout.secs |
field `secs` of struct `Duration` is private |
방법 1 (as_secs()) |
account.balance = 500 |
똑같이 E0616. 쓰기도 막혀요 | 방법 2 |
두 번째 줄의 help는 “len이라는 메서드도 있으니 괄호를 붙여 호출하라”는 뜻이에요.
무슨 뜻인가요
섹션 제목: “무슨 뜻인가요”화살표는 점 뒤의 필드 이름을 가리켜요. 같은 줄의 account.owner는 괜찮아요. owner에는 pub이 붙어 있거든요. balance에는 없어서, mod bank 안의 코드만 읽거나 바꿀 수 있어요.
Account 자체는 pub이라는 점을 보세요. 사람들이 놀라는 부분이 바로 여기예요. 구조체를 공개해도 필드까지 공개되지는 않아요. 필드마다 따로 pub을 붙이지 않으면 비공개로 남아요.
은행을 떠올려 보세요. 은행에 들어갈 수 있고, 계좌에 적힌 이름은 누구나 볼 수 있어요. 하지만 창구 안으로 들어가 금고를 열고 돈을 셀 수는 없어요. 직원에게 부탁하면, 직원이 은행의 규칙에 따라 처리해 줘요.
Rust가 이렇게 하는 이유
섹션 제목: “Rust가 이렇게 하는 이유”Rust에서는 모든 것이 기본적으로 비공개예요. 함수, 모듈, 그리고 구조체 필드도요(모듈 참고). 공개 여부는 타입이 아니라 모듈 단위로 정해져요. mod bank 안의 코드라면 누구든 balance를 건드릴 수 있고, 밖의 코드는 못 해요.
목적은 구조체가 자기 규칙을 스스로 지키게 하는 거예요. 은행 잔액은 절대 음수가 되면 안 된다고 해 볼게요. 프로그램의 모든 곳에서 account.balance = ...라고 쓸 수 있다면, 어디서든 그 규칙을 깰 수 있고, 누가 깼는지 찾으려면 코드 전체를 뒤져야 해요. 필드가 비공개라면 mod bank 안의 몇몇 메서드만 값을 바꿀 수 있어요. 거기서 규칙을 확인하면 어디서나 규칙이 지켜져요. 이 생각을 캡슐화(encapsulation)라고 해요.
표준 라이브러리도 똑같이 해요. Vec은 길이와 메모리 버퍼를 비공개 필드로 갖고 있어요. 항목이 3개뿐인 벡터에 v.len = 100이라고 쓸 수 있다면, v[50]을 읽을 때 엉뚱한 메모리를 읽게 될 거예요. len을 비공개로 두기 때문에 Vec을 안전하게 쓸 수 있는 거예요.
고치는 방법
섹션 제목: “고치는 방법”방법 1: 필드 대신 메서드 호출하기
섹션 제목: “방법 1: 필드 대신 메서드 호출하기”구조체가 표준 라이브러리나 다른 크레이트에서 왔을 때 이 방법을 고르세요. 그 코드는 내가 바꿀 수 없고, 보여 줘도 되는 것에는 늘 메서드를 마련해 두거든요. rustc가 help: a method ... also exists(메서드도 있다)라고 하면 ()를 붙이면 돼요.
use std::time::Duration;
fn main() { let scores = vec![10, 20, 30]; println!("점수 {}개", scores.len());
let timeout = Duration::from_secs(90); println!("제한 시간: {}초", timeout.as_secs());}실행 결과:
점수 3개제한 시간: 90초메서드 이름이 필드 이름과 늘 같지는 않아요(secs는 as_secs()로 읽어요). docs.rs에서 그 타입의 페이지를 보거나, 점을 찍은 뒤 에디터가 보여 주는 메서드 목록을 확인해 보세요.
방법 2: 게터와 규칙을 지키는 메서드 추가하기
섹션 제목: “방법 2: 게터와 규칙을 지키는 메서드 추가하기”내가 만든 구조체이고, 필드에 지켜야 할 규칙이 있을 때 이 방법을 고르세요. 필드는 비공개로 두고, 값을 돌려주는 pub fn(게터, getter)을 추가한 다음, 규칙을 확인하는 메서드로만 값을 바꾸세요.
mod bank { pub struct Account { pub owner: String, balance: i64, // 비공개: 이 모듈만 바꿀 수 있어요 }
impl Account { pub fn new(owner: &str) -> Account { Account { owner: owner.to_string(), balance: 0 } }
// 게터: 잔액은 누구나 읽을 수 있어요. pub fn balance(&self) -> i64 { self.balance }
pub fn deposit(&mut self, amount: i64) { if amount > 0 { self.balance += amount; } }
// 규칙은 여기에 있어요. 잔액은 절대 0보다 작아지지 않아요. pub fn withdraw(&mut self, amount: i64) -> Result<(), String> { if amount > self.balance { return Err(format!("{}만 남아 있어요", self.balance)); } self.balance -= amount; Ok(()) } }}
fn main() { let mut account = bank::Account::new("Ferris"); account.deposit(100); println!("{}의 잔액: {}", account.owner, account.balance());
match account.withdraw(500) { Ok(()) => println!("500을 출금했어요"), Err(e) => println!("500을 출금할 수 없어요: {e}"), } println!("잔액은 그대로: {}", account.balance());}실행 결과:
Ferris의 잔액: 100500을 출금할 수 없어요: 100만 남아 있어요잔액은 그대로: 100Rust에서는 메서드와 필드가 같은 이름을 가져도 돼요. 그래서 게터 이름은 보통 get_balance()가 아니라 balance()로 지어요. 게터를 만들고 나면 rustc가 직접 제안해 주기도 해요. help: a method `balance` also exists, call it with parentheses(메서드 balance도 있으니 괄호를 붙여 호출하라)처럼요. deposit과 withdraw는 세터(setter) 역할을 해요. 필드를 바꾸되, 규칙이 허락하는 방식으로만 바꿔요. 메서드에 대해서는 구조체에서 더 볼 수 있어요.
방법 3: 단순한 데이터라면 필드를 pub으로
섹션 제목: “방법 3: 단순한 데이터라면 필드를 pub으로”어떤 값이 와도 괜찮고, 그 필드에 기대는 다른 것이 없을 때 이 방법을 고르세요. 격자 위의 점, 색상, 설정 구조체에는 지킬 규칙이 없어요. 이런 곳에 게터를 만드는 건 타이핑만 늘릴 뿐이에요.
mod geometry { pub struct Point { pub x: i32, pub y: i32, }}
fn main() { let mut p = geometry::Point { x: 3, y: 4 }; p.x += 1; println!("점의 위치: ({}, {})", p.x, p.y);}실행 결과:
점의 위치: (4, 4)내 코드에서만 필드를 볼 수 있으면 될 때는 pub(crate) x: i32(이 크레이트의 모든 모듈)나 pub(super) x: i32(부모 모듈만)라고 쓰세요. 프로그램에서는 pub처럼 동작하고, 라이브러리에서는 그 필드를 공개 API에서 빼 줘요. 모든 단계를 정리한 표는 E0603 페이지에 있어요.
그냥 pub으로 만들어도 될까요? 이렇게 물어보세요. “이 필드에 어떤 값이 들어가거나, 이 필드만 따로 바뀌면 구조체가 잘못될 수 있을까?” 음수 잔액, 실제 데이터와 맞지 않는 len, 항목의 합과 더 이상 같지 않은 total 같은 경우예요. 이런 필드는 비공개로 두고 방법 2를 쓰세요. 답이 “아니, 어떤 값이든 괜찮아”라면 pub이 솔직한 선택이에요.
자주 보게 되는 곳
섹션 제목: “자주 보게 되는 곳”Vec의 len. scores.len이라고 썼더니 field `len` of struct `Vec` is private가 나왔어요. Vec에는 정말로 len이라는 비공개 필드가 있기 때문이에요. 해결은 scores.len()이에요. 재미있는 점이 하나 있어요. String에 name.len이라고 쓰면 다른 에러인 E0615(attempted to take value of method, 메서드의 값을 가져가려 함)가 나요. String에는 그런 이름의 필드가 없거든요. 해결 방법은 같아요.
밖에서 필드 값을 바꿀 때. account.balance = 500;도 똑같이 E0616이에요. 쓰기도 막혀 있어요. 값을 바꾸려고 필드를 pub으로 만들지 말고, deposit처럼 값을 바꾸면서 확인도 하는 메서드를 추가하세요.
구조체를 모듈이나 파일로 옮긴 뒤. main.rs의 코드가 몇 달 동안 user.email을 잘 써 왔어요. 그런데 User를 src/user.rs로 옮기자 필드에 접근하는 곳이 전부 깨졌어요. 전에는 괜찮던 필드가 이제 mod user의 비공개 필드가 된 거예요. 단순한 데이터인 필드에는 pub을 붙이고, 나머지에는 게터를 추가하세요.
테스트. 같은 파일 안의 #[cfg(test)] mod tests 블록은 자식 모듈이라서 비공개 필드를 읽을 수 있어요. tests/ 폴더의 테스트는 크레이트 바깥에 있어서, 다른 사용자처럼 pub API만 쓸 수 있어요.
관련 에러
섹션 제목: “관련 에러”- E0603:
function `x` is private. 함수, 구조체, 모듈에 대한 같은 개념이에요. - E0451: 같은 비공개 필드지만,
Account { ... }로 구조체를 만들 때 나요. - E0624:
method `x` is private. 메서드(또는new)에도pub이 필요해요. - E0609:
no field `x` on type `T`. 필드가 아예 없어요(오타인 경우가 많아요). - E0615:
attempted to take value of method.x.len()을 쓰려다x.len이라고 썼어요. - 전체 컴파일러 에러: 에러 페이지 전체 목록이에요.
이해도 점검
섹션 제목: “이해도 점검”이해도 점검
1. mod bank 안에 pub struct Account { balance: u64 }가 있어요. main에서 account.balance를 읽을 수 있을까요?
2. `field len of struct Vec is private`가 나왔어요. 어떻게 고칠까요?
3. Score 구조체의 points 필드는 항상 0에서 100 사이여야 해요. 가장 좋은 설계는 무엇일까요?
점수: 0 / 3
더 알아보기
섹션 제목: “더 알아보기”- E0616 — Rust error codes index (영어): 짧은 예제가 있는 이 에러의 공식 설명이에요.
- 경로를 사용하여 모듈 트리의 아이템 참조하기 — The Rust Programming Language 한국어판:
pub struct인데도 필드가 비공개인 이유를 레스토랑 예제로 설명해요. - 객체 지향 언어의 특성 — The Rust Programming Language 한국어판: 필드를 비공개로 두어 평균값이 늘 올바르게 유지되는 구조체를 보여 줘요.
- Struct visibility — Rust by Example (영어): 공개 필드와 비공개 필드를 실행 가능한 예제 하나에 나란히 보여 줘요.
- Structs have private fields (C-STRUCT-PRIVATE) — Rust API Guidelines (영어): 라이브러리가 나중에 필드를 바꿀 수 있도록 필드를 비공개로 두는 이유를 설명해요.