콘텐츠로 이동

Rust 에러 E0624: method is private (비공개 메서드)

E0624는 호출한 메서드가 있긴 하지만 pub 없이 작성되어서, 그 모듈 안의 코드만 호출할 수 있다는 뜻이에요. 구조체가 공개여도, 모듈이 공개여도 상관없어요. 메서드는 하나하나 비공개로 시작해요. 타입을 따로 모듈로 옮긴 직후에 아주 흔하게 만나는 에러이고, 해결책은 보통 fn 앞에 pub을 붙이는 거예요. 이 페이지는 Rust 컴파일러 에러 해설의 일부예요.

error[E0624]: associated function `new` is private
--> src/main.rs:18:38
|
7 | fn new() -> Account { // `pub`이 없어요
| ------------------- private associated function defined here
...
18 | let mut account = bank::Account::new(); // ❌ 비공개 연관 함수
| ^^^ private associated function
error[E0624]: method `add_interest` is private
--> src/main.rs:19:13
|
11 | fn add_interest(&mut self) { // `pub`이 없어요
| -------------------------- private method defined here
...
19 | account.add_interest(); // ❌ 비공개 메서드
| ^^^^^^^^^^^^ private method

첫 번째 에러는 “연관 함수 new는 비공개”, 두 번째는 “메서드 add_interest는 비공개”라는 뜻이에요. defined here 라벨은 “비공개 함수(메서드)가 여기에 정의되어 있다”고 알려 줘요.

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

mod bank {
pub struct Account {
balance: u64,
}
impl Account {
fn new() -> Account { // `pub`이 없어요
Account { balance: 0 }
}
fn add_interest(&mut self) { // `pub`이 없어요
self.balance += self.balance / 100;
}
}
}
fn main() {
let mut account = bank::Account::new(); // ❌ 비공개 연관 함수
account.add_interest(); // ❌ 비공개 메서드
}

rustc는 여기서 이름을 두 가지로 써요. 메서드(method)는 self를 받아요(account.add_interest()). 연관 함수(associated function)는 self가 없고 ::로 호출해요(Account::new()). 규칙과 해결 방법은 둘 다 똑같아요.

에러마다 화살표가 두 개 있어요. ^^^ 화살표는 내가 호출한 곳이에요. --- 화살표는 pub이 빠진 fn 줄을 보여 줘요. 그러니까 메시지가 pub을 어디에 붙여야 하는지 정확히 알려 주는 거예요.

impl Account 안에 있다고 메서드가 공개되지는 않아요. Account가 pub이어도 마찬가지예요. 식당을 떠올려 보세요. 메뉴판은 공개되어 있어서 요리를 주문할 수 있어요(pub 메서드). 주방에는 “양파 썰기” 같은 자기만의 과정이 있는데, 손님이 그걸 직접 시킬 수는 없어요. 그게 비공개 메서드예요.

Rust에서는 모든 것이 기본적으로 비공개이고, 메서드도 예외가 아니에요(모듈 참고). 공개 여부는 impl 블록이 작성된 모듈에 속해요. mod bank 안의 코드라면 누구든 add_interest를 호출할 수 있고, 밖의 코드는 못 해요.

덕분에 타입은 올바른 순서로 불려야만 의미가 있는 작은 도우미 메서드를 가질 수 있어요. add_interest는 한 달에 한 번, 수수료를 뗀 다음에 실행되어야 한다고 해 볼게요. 누구나 호출할 수 있다면 누군가 실수로 두 번 호출할 거예요. 비공개 도우미들과, 그것들을 올바르게 실행하는 공개 메서드 하나가 있으면 그런 실수는 아예 일어날 수 없어요.

선택의 폭도 넓어져요. 비공개 메서드는 언제든 이름을 바꾸거나, 합치거나, 지울 수 있어요. 바깥 코드가 그걸 쓰지 않는다는 걸 컴파일러가 보장해 주거든요.

메서드가 밖에서 호출하라고 만든 것일 때 이 방법을 고르세요. new라면 거의 항상 여기에 해당해요. 아무도 호출할 수 없는 생성자는 쓸모가 별로 없으니까요.

mod bank {
pub struct Account {
balance: u64,
}
impl Account {
pub fn new(opening: u64) -> Account {
Account { balance: opening }
}
pub fn add_interest(&mut self) {
self.balance += self.balance / 100; // 1%
}
pub fn balance(&self) -> u64 {
self.balance
}
}
}
fn main() {
let mut account = bank::Account::new(1000);
account.add_interest();
println!("잔액: {}", account.balance());
}

실행 결과:

잔액: 1010
▶ 직접 실행해 보기

방법 2: 나를 위해 마련된 공개 메서드 호출하기

섹션 제목: “방법 2: 나를 위해 마련된 공개 메서드 호출하기”

비공개 메서드가 도우미일 때, 특히 내가 쓰지 않은 코드에서 이 방법을 고르세요. 그 도우미를 올바른 순서로, 필요한 확인까지 하면서 대신 호출해 주는 공개 메서드가 있을 때가 많아요. 여기서는 add_interest를 비공개로 두는 게 올바른 설계예요.

mod bank {
pub struct Account {
balance: u64,
}
impl Account {
pub fn new(opening: u64) -> Account {
Account { balance: opening }
}
// 공개된 입구: 한 번 호출로 한 달 처리를 올바른 순서로 끝내요.
pub fn close_month(&mut self) {
self.charge_fee();
self.add_interest();
}
pub fn balance(&self) -> u64 {
self.balance
}
// 비공개 도우미: 따로 부르거나 두 번 부르면 결과가 틀려져요.
fn charge_fee(&mut self) {
self.balance = self.balance.saturating_sub(5);
}
fn add_interest(&mut self) {
self.balance += self.balance / 100;
}
}
}
fn main() {
let mut account = bank::Account::new(1000);
account.close_month();
println!("한 달 뒤 잔액: {}", account.balance());
// account.add_interest(); // ❌ E0624: method `add_interest` is private
}

실행 결과:

한 달 뒤 잔액: 1004
▶ 직접 실행해 보기

좋은 기준이 있어요. 언제든, 그것만 따로 호출해도 구조체가 망가지지 않을 때만 메서드를 pub으로 만드세요. 더 큰 작업의 한 단계로서만 의미가 있다면, 비공개로 두고 그 큰 작업을 공개하세요.

방법 3: “내 코드에서만” 쓰려면 pub(crate) 쓰기

섹션 제목: “방법 3: “내 코드에서만” 쓰려면 pub(crate) 쓰기”

내 크레이트의 다른 모듈은 그 메서드가 필요하지만, 내 라이브러리를 쓰는 사람들은 쓰면 안 될 때 이 방법을 고르세요. pub(super)(부모 모듈만)도 같은 방식으로 동작해요. 모든 단계를 정리한 표는 E0603 페이지에 있어요.

mod shop {
pub struct Cart {
prices: Vec<u32>,
}
impl Cart {
pub fn new() -> Cart {
Cart { prices: Vec::new() }
}
pub fn add(&mut self, price: u32) {
self.prices.push(price);
}
// 영수증처럼 우리 코드에서만 써요. 라이브러리 사용자용이 아니에요.
pub(crate) fn total(&self) -> u32 {
self.prices.iter().sum()
}
}
}
mod receipt {
pub fn print(cart: &crate::shop::Cart) {
println!("합계: {}", cart.total());
}
}
fn main() {
let mut cart = shop::Cart::new();
cart.add(3);
cart.add(5);
receipt::print(&cart);
}

실행 결과:

합계: 8
▶ 직접 실행해 보기

주의할 점이 하나 있어요. src/lib.rs와 src/main.rs가 둘 다 있는 패키지에서는 둘이 서로 다른 크레이트예요. lib.rs의 pub(crate) fn total은 main.rs에서 보면 비공개예요.

error[E0624]: method `total` is private
--> src/main.rs:3:25
|
3 | println!("{}", cart.total());
| ^^^^^ private method
|
::: src/lib.rs:10:5
|
10 | pub(crate) fn total(&self) -> u32 {
| --------------------------------- private method defined here

“메서드 total은 비공개”라는 뜻이고, 아래쪽 라벨이 src/lib.rs에 있는 정의를 가리켜요. 이럴 때는 그냥 pub을 쓰세요. Cargo와 크레이트를 참고하세요.

방법 4: 그 메서드가 필요한 코드를 모듈 안으로 옮기기

섹션 제목: “방법 4: 그 메서드가 필요한 코드를 모듈 안으로 옮기기”

메서드는 정말 비공개로 남아야 하는데, 감사(audit) 검사나 테스트처럼 그래도 그걸 써야 하는 코드가 있을 때 이 방법을 고르세요. 같은 모듈의 코드는 호출할 수 있고, 자식 모듈도 호출할 수 있어요. bank 안의 모듈들은 bank의 비공개 항목을 전부 볼 수 있거든요.

mod bank {
pub struct Account {
balance: u64,
}
impl Account {
pub fn new(opening: u64) -> Account {
Account { balance: opening }
}
fn is_suspicious(&self) -> bool {
self.balance > 10_000
}
}
// 자식 모듈은 부모의 비공개 항목을 전부 쓸 수 있어요.
pub mod audit {
pub fn check(account: &super::Account) -> &'static str {
if account.is_suspicious() { "확인 필요" } else { "정상" }
}
}
}
fn main() {
let small = bank::Account::new(500);
let big = bank::Account::new(50_000);
println!("작은 계좌: {}", bank::audit::check(&small));
println!("큰 계좌: {}", bank::audit::check(&big));
}

실행 결과:

작은 계좌: 정상
큰 계좌: 확인 필요
▶ 직접 실행해 보기

단위 테스트도 똑같이 동작해요. bank 안에(또는 src/bank.rs의 맨 아래에) 작성한 mod tests는 자식 모듈이라서 비공개 메서드를 호출할 수 있어요. 테스트를 참고하세요.

#[cfg(test)]
mod tests {
use super::*;
#[test]
fn not_suspicious_when_small() {
let account = Account::new(500);
assert!(!account.is_suspicious()); // 비공개지만 여기서는 괜찮아요
}
}

다른 모듈에 있는 impl 블록. 공개 여부는 구조체가 아니라 impl이 작성된 모듈에 속해요. Order는 mod model에 있는데 mod pricing 안에 impl Order { fn with_tax(&self) ... }를 썼다면, main에서는 method `with_tax` is private가 나고 화살표는 mod pricing을 가리켜요. 해결: pub fn with_tax나 pub(crate)를 쓰세요.

메서드를 함수 값으로 쓸 때. accounts.iter().map(bank::Account::audit)는 같은 호출을 다르게 쓴 것뿐이라서 똑같이 E0624가 나요. 해결 방법도 같아요.

트레이트 메서드. impl SomeTrait for Type 안의 메서드에는 절대 pub을 쓰지 않아요. 트레이트만큼 공개되어 있고, pub을 붙이면 오히려 에러예요. error[E0449]: visibility qualifiers are not permitted here(여기에는 공개 범위 지정자를 쓸 수 없음)가 나요. 트레이트 메서드가 없다고 나온다면 대신 E0599가 나고, 그 트레이트를 use하면 해결돼요.

크레이트의 비공개 메서드. GitHub에서 크레이트 소스 코드를 보다가 딱 맞는 메서드를 찾았는데, pub이 아니에요. 호출할 수 없고, 작성자가 어느 릴리스에서든 바꿀 수 있어요. docs.rs의 크레이트 페이지에서 같은 일을 하는 공개 메서드를 찾아보세요. 거기에는 pub 항목만 나와요.

  • E0603: function `x` is private. 일반 함수, 구조체, 모듈에 대한 같은 개념이에요.
  • E0616: field `x` of struct `Y` is private. 필드를 읽거나 쓸 때 나요.
  • E0451: Y { ... }로 구조체를 만들 때 비공개 필드가 있어요. 보통 pub fn new로 해결해요.
  • E0599: no method named `x` found. 그 타입에 메서드가 없거나, 트레이트가 스코프에 없어요.
  • 전체 컴파일러 에러: 에러 페이지 전체 목록이에요.

이해도 점검

1. mod clock 안에 pub struct Timer와 impl Timer { fn start() -> Timer { ... } }가 있어요. main에서 clock::Timer::start()를 호출하면 어떻게 될까요?

2. mod bank에 정의된 비공개 메서드는 어떤 코드가 호출할 수 있을까요?

3. 비공개 add_interest는 한 달에 한 번, 수수료를 뗀 뒤에만 실행되어야 해요. E0624를 고치는 가장 좋은 방법은 무엇일까요?

점수: 0 / 3