Rust 에러 E0603: function, struct or module is private (비공개 항목)
E0603은 알고 보면 좋은 소식이에요. Rust가 내가 원한 함수, 구조체, 모듈을 찾기는 했어요. 다만 그 항목이 비공개(private)라서 건네줄 수가 없는 거예요. Rust 모듈 안의 모든 것은 따로 말하지 않는 한 비공개라서, 코드를 처음 모듈로 나눌 때 거의 모든 사람이 이 에러를 만나요. 해결책은 보통 pub 한 단어예요. 이 페이지는 Rust 컴파일러 에러 해설의 일부예요.
에러 메시지
섹션 제목: “에러 메시지”error[E0603]: function `apply_discount` is private --> src/main.rs:8:26 |8 | let total = billing::apply_discount(200); // ❌ 비공개 | ^^^^^^^^^^^^^^ private function |note: the function `apply_discount` is defined here --> src/main.rs:2:5 |2 | fn apply_discount(price: u32) -> u32 { // `pub`이 없어요 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^“함수 apply_discount는 비공개”라는 뜻이에요. note는 “함수 apply_discount는 여기에 정의되어 있다”며 정의된 자리를 보여 줘요.
이 에러를 내는 프로그램은 다음과 같아요.
mod billing { fn apply_discount(price: u32) -> u32 { // `pub`이 없어요 price * 90 / 100 }}
fn main() { let total = billing::apply_discount(200); // ❌ 비공개 println!("합계: {total}");}첫 줄에 항목의 종류가 나오기 때문에, 같은 에러가 몇 가지 모양으로 나타나요.
| 첫 줄에 나오는 문구 | 보통 무엇이 잘못됐나요 | 해결 |
|---|---|---|
function `apply_discount` is private |
fn에 pub이 없음 |
방법 1 또는 방법 2 |
struct `Order` is private |
struct에 pub이 없음 |
방법 1 |
module `prices` is private |
경로에 있는 mod에 pub이 없음 |
방법 1 또는 방법 3 |
function import `price_of` is private |
다른 모듈 안의 일반 use를 거쳐 항목에 접근함 |
방법 3 |
tuple struct constructor `Percent` is private |
구조체는 pub이지만 필드가 pub이 아님 |
방법 4 |
무슨 뜻인가요
섹션 제목: “무슨 뜻인가요”두 화살표를 읽어 보세요. 첫 번째(8번째 줄)는 항목을 쓰는 곳이에요. note:의 화살표(2번째 줄)는 항목이 정의된 곳이에요. Rust는 이렇게 말하고 있어요. “저기 apply_discount가 보이긴 하는데, 공유한다고 표시한 적이 없으니 billing 밖의 코드는 호출할 수 없어요.”
파일은 상관없어요. billing이 src/billing.rs에 있어도 같은 에러가 나고, note:가 대신 src/billing.rs:1:1을 가리킬 뿐이에요.
사무실 건물을 떠올려 보세요. 각 모듈은 문이 달린 방이에요. 비공개 항목은 방 안에만 있는 도구예요. 방 안에 있는 사람들(그리고 그 안의 더 작은 방에 있는 사람들)은 쓸 수 있지만, 복도에 있는 사람은 아무도 못 써요. pub은 그 도구를 복도 선반에 올려 두는 거예요.
Rust가 이렇게 하는 이유
섹션 제목: “Rust가 이렇게 하는 이유”Rust는 모든 것을 기본적으로 비공개로 만들어요. 함수, 구조체, 구조체 필드, 모듈, 심지어 use로 가져온 이름까지요. 공유하고 싶을 때 pub으로 직접 선택해요. 모듈 강좌에서도 처음부터 이 내용을 다뤄요.
이유는 미래의 나를 지키기 위해서예요. pub인 것은 하나의 약속이 돼요. 다른 코드가 거기에 기댈 수 있으니, 바꾸면 그 코드가 망가질 수 있어요. 비공개 항목은 마음대로 바꾸고, 이름을 고치고, 지워도 돼요. 밖에서 아무도 그것에 기대지 않는다는 걸 컴파일러가 보장해 주거든요. 작은 도우미 함수는 도우미로 남고, 구조체는 아무도 필드를 직접 건드릴 수 없으니 자기 규칙(예를 들어 “잔액은 절대 음수가 아니다”)을 지킬 수 있어요.
하나 더 있어요. 공개 여부는 경로의 모든 단계에서 확인해요. shop::prices::price_of는 prices가 나에게 보이고 그리고 price_of도 나에게 보일 때만 동작해요. 비공개 모듈 안에 있는 pub fn은 여전히 손이 닿지 않아요.
고치는 방법
섹션 제목: “고치는 방법”방법 1: 항목에 pub 붙이기(경로의 모든 모듈에도)
섹션 제목: “방법 1: 항목에 pub 붙이기(경로의 모든 모듈에도)”항목을 모듈 밖에서 쓰도록 만든 거라면 이 방법을 고르세요. 대부분은 이게 정답이에요.
mod billing { pub fn apply_discount(price: u32) -> u32 { price * 90 / 100 }}
fn main() { let total = billing::apply_discount(200); println!("합계: {total}");}실행 결과:
합계: 180에러가 module `prices` is private(모듈 prices는 비공개)라고 말한다면, 빠진 pub은 모듈에 있어요. pub mod prices { ... }라고 쓰세요. 이 경우 rustc는 function `price_of` is not publicly re-exported(함수 price_of가 공개적으로 다시 내보내지지 않음)라는 두 번째 라벨로도 알려 줘요. 파일을 따로 나눴다면 pub은 선언 줄에 붙여요. pub mod prices;처럼요.
구조체라면 pub struct Order는 타입을 공개할 뿐이에요. 밖의 코드가 필드를 읽거나 바꿔야 한다면 필드마다 따로 pub이 필요해요. 이건 방법 4에서 더 다룰게요.
방법 2: “내 코드에서만” 쓰려면 pub(crate)나 pub(super) 쓰기
섹션 제목: “방법 2: “내 코드에서만” 쓰려면 pub(crate)나 pub(super) 쓰기”항목을 공유해야 하지만 온 세상과 공유할 필요는 없을 때 이 방법을 고르세요. pub에는 몇 가지 단계가 있어요. 좁은 것부터 넓은 것 순서예요.
| 이렇게 쓰면 | 항목을 쓸 수 있는 곳 |
|---|---|
| 아무것도 안 씀(비공개) | 자기 모듈과 그 안의 모듈들 |
pub(super) |
여기에 부모 모듈까지 |
pub(crate) |
이 크레이트의 모든 코드 |
pub |
모두. 내 크레이트에 의존하는 다른 크레이트까지 |
프로그램(바이너리)에서는 pub(crate)와 pub이 거의 똑같이 동작해요. 차이는 라이브러리에서 드러나요. pub(crate)를 쓰면 도우미 함수를 라이브러리의 공개 API로 만들지 않고도 내 모듈끼리 공유할 수 있어요.
mod shop { // 이 크레이트의 어떤 코드든 호출할 수 있어요. pub(crate) fn store_name() -> &'static str { "Ferris Fruits" }
pub mod receipt { // 이 도우미는 부모 모듈인 `shop`만 호출할 수 있어요. pub(super) fn line(item: &str, price: u32) -> String { format!("{item}: {price}") } }
pub fn print_receipt(items: &[(&str, u32)]) { println!("== {} ==", store_name()); for (item, price) in items { println!("{}", receipt::line(item, *price)); } }}
fn main() { println!("{}에 오신 걸 환영해요", shop::store_name()); shop::print_receipt(&[("apple", 3), ("pear", 5)]); // shop::receipt::line("apple", 3); // ❌ E0603: `line`은 `shop` 전용이에요}실행 결과:
Ferris Fruits에 오신 걸 환영해요== Ferris Fruits ==apple: 3pear: 5마지막 줄 앞의 //를 지우면, pub(super)라고 썼는데도 function `line` is private가 나요. main은 receipt의 부모가 아니라서 문이 닫혀 있는 거예요.
방법 3: pub use로 다시 내보내기
섹션 제목: “방법 3: pub use로 다시 내보내기”안쪽 모듈은 내부 사정이지만, 그 안의 항목 하나는 쉽게 쓸 수 있어야 할 때 이 방법을 고르세요. 모듈은 비공개로 두고, 항목만 더 짧은 공개 경로에 올려 두는 거예요.
mod shop { mod prices { // 비공개로 둬요. 가격을 어떻게 저장하는지는 shop이 알아서 할 일이에요 pub fn price_of(item: &str) -> u32 { if item == "apple" { 3 } else { 5 } } }
pub use prices::price_of; // 이제 shop::price_of로 쓸 수 있어요}
fn main() { println!("사과 가격: {}", shop::price_of("apple")); println!("배 가격: {}", shop::price_of("pear"));}실행 결과:
사과 가격: 3배 가격: 5function import `price_of` is private(가져온 함수 price_of는 비공개)도 이 방법으로 고쳐요. shop 안의 일반 use prices::price_of;는 shop만을 위한 지름길이에요. 다른 모든 것처럼 use도 비공개거든요. pub use는 그 지름길을 공개된 문으로 바꿔 줘요. 큰 크레이트들은 늘 이렇게 해요. 그래서 serde::Serialize가 어느 안쪽 모듈에 있는지 몰라도 그냥 쓸 수 있는 거예요.
방법 4: 필드를 공개하는 대신 pub fn new 추가하기
섹션 제목: “방법 4: 필드를 공개하는 대신 pub fn new 추가하기”에러가 구조체의 필드나 생성자에 관한 것일 때 이 방법을 고르세요. 여기서는 구조체는 pub이지만, 필드는 아니에요.
mod billing { pub struct Percent(u8);}
fn main() { let discount = billing::Percent(150); // ❌ 비공개 생성자}error[E0603]: tuple struct constructor `Percent` is private --> src/main.rs:6:29 |2 | pub struct Percent(u8); | -- a constructor is private if any of the fields is private...6 | let discount = billing::Percent(150); // ❌ 비공개 생성자 | ^^^^^^^ private tuple struct constructor |help: consider making the field publicly accessible |2 | pub struct Percent(pub u8); | +++“튜플 구조체 생성자 Percent는 비공개”라는 뜻이에요. 라벨은 “필드 중 하나라도 비공개면 생성자도 비공개”라고 설명하고, help는 “필드를 공개해 보라”고 제안해요.
rustc는 pub u8을 제안하고, 단순한 데이터 타입이라면 그래도 괜찮아요. 그런데 값을 보세요. 150퍼센트예요! 필드가 비공개인 것 자체가 핵심일 때가 많아요. 모듈은 Percent가 만들어지기 전에 값을 확인하고 싶은 거예요. 확인까지 해 주는 공개 생성자를 만들어 주세요.
mod billing { pub struct Percent(u8);
impl Percent { pub fn new(value: u8) -> Option<Percent> { if value <= 100 { Some(Percent(value)) } else { None } }
pub fn apply(&self, price: u32) -> u32 { price * (100 - self.0 as u32) / 100 } }}
fn main() { let ten = billing::Percent::new(10).unwrap(); println!("200에서 10% 할인: {}", ten.apply(200)); println!("150%도 되나요? {}", billing::Percent::new(150).is_some());}실행 결과:
200에서 10% 할인: 180150%도 되나요? false이제 프로그램 전체의 모든 Percent는 0에서 100 사이라는 게 보장되고, apply는 절대 언더플로가 나지 않아요. new 자체에도 pub이 필요하다는 걸 잊지 마세요. 비공개 메서드는 다른 에러인 E0624(associated function `new` is private, 연관 함수 new는 비공개)를 내요. 생성자에 대해서는 구조체에서 더 볼 수 있어요.
자주 보게 되는 곳
섹션 제목: “자주 보게 되는 곳”main.rs와 lib.rs가 둘 다 있는 패키지. 이 둘은 서로 다른 크레이트이고, main.rs는 라이브러리를 바깥에서 써요. lib.rs에 mod inventory;라고만 쓰면, main.rs의 use shop::inventory::count_items;는 실패해요.
error[E0603]: module `inventory` is private --> src/main.rs:1:11 |1 | use shop::inventory::count_items; | ^^^^^^^^^ private module“모듈 inventory는 비공개”라는 뜻이에요. 해결: lib.rs에 pub mod inventory;라고 쓰고, 모듈 안에서는 pub fn count_items라고 쓰세요. 주의하세요. 여기서는 pub(crate)로는 부족해요. 라이브러리 입장에서 main.rs는 다른 크레이트라서, pub(crate) fn이어도 function `count_items` is private가 나요. Cargo와 크레이트를 참고하세요.
비공개 구조체 필드. 이건 따로 에러 코드가 있어요. 비공개 필드가 있는 구조체를 만들면 E0451, 그 필드를 읽으면 E0616이 나요.
error[E0451]: field `balance` of struct `Account` is privateerror[E0616]: field `balance` of struct `Account` is private둘 다 “구조체 Account의 필드 balance는 비공개”라는 뜻이에요. 해결: 방법 4와 같은 선택이에요. 누구나 바꿔도 된다면 필드에 pub을 붙이고, 아니면 비공개로 두고 pub fn new(...)와 pub fn balance(&self) -> u64 같은 게터를 추가하세요.
크레이트의 소스 코드에서 찾은 함수. GitHub에서 크레이트 코드를 읽다가 딱 맞는 도우미 함수를 찾았는데, 호출하니 E0603이 나요. 크레이트 작성자가 pub으로 만들지 않았다면 쓸 수 없고, 그 함수는 언제든 바뀔 수 있어요. docs.rs의 크레이트 페이지에서 같은 일을 하는 공개된 방법을 찾아보세요. 거기에는 pub 항목만 나와요.
관련 에러
섹션 제목: “관련 에러”- E0432:
unresolved import. 항목을 아예 찾지 못했어요. E0603은 찾았지만 비공개인 경우예요. - E0433:
failed to resolve. 코드 안의 경로가 아무 데로도 이어지지 않아요. - E0451과 E0616: 구조체 필드가 비공개예요(구조체를 만들 때, 또는 필드를 읽을 때).
- E0624: 메서드나 연관 함수(
new같은)가 비공개예요. - 전체 컴파일러 에러: 에러 페이지 전체 목록이에요.
이해도 점검
섹션 제목: “이해도 점검”이해도 점검
1. mod shop { mod prices { pub fn price_of() {} } }이 있고 main이 shop::prices::price_of()를 호출해요. 어떤 에러가 날까요?
2. src/lib.rs의 도우미 함수에 pub(crate)가 붙어 있어요. 같은 패키지의 src/main.rs에서 호출할 수 있을까요?
3. pub struct Email(String);은 일부러 필드를 비공개로 뒀어요. 다른 모듈에서 값을 만들 수 있게 하는 친절한 방법은 무엇일까요?
점수: 0 / 3
더 알아보기
섹션 제목: “더 알아보기”- E0603 — Rust error codes index (영어): 짧은 예제가 있는 이 에러의 공식 설명이에요.
- 경로를 사용하여 모듈 트리의 아이템 참조하기 — The Rust Programming Language 한국어판:
pub으로 경로를 공개하는 법과, 구조체와 열거형에서pub이 어떻게 동작하는지 설명해요. - use 키워드로 경로를 스코프 안으로 가져오기 — The Rust Programming Language 한국어판:
pub use로 이름을 다시 내보내는 법을 다뤄요. - Visibility — Rust by Example (영어):
pub,pub(crate),pub(super),pub(in path)를 실행 가능한 예제 하나로 보여 줘요. - Visibility and privacy — The Rust Reference (영어): 전체 그림을 알고 싶을 때 볼 수 있는 정확한 규칙이에요.