Rustのエラー E0624: method is private(非公開のメソッド)
E0624 は、呼んだメソッドはあるけれど、pub を付けずに書かれているので、そのモジュールの中のコードからしか呼べない という意味のエラーです。構造体が公開されていても、モジュールが公開されていても、メソッドは1つずつ、非公開から始まります。型を自分のモジュールに移した直後にとてもよく出会うエラーで、たいていは fn の前に pub を付ければ直ります。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。
エラーメッセージ
Section titled “エラーメッセージ”error[E0624]: associated function `new` is private --> src/main.rs:18:38 | 7 | fn new() -> Account { // no `pub` | ------------------- private associated function defined here...18 | let mut account = bank::Account::new(); // ❌ private associated function | ^^^ private associated function
error[E0624]: method `add_interest` is private --> src/main.rs:19:13 |11 | fn add_interest(&mut self) { // no `pub` | -------------------------- private method defined here...19 | account.add_interest(); // ❌ private method | ^^^^^^^^^^^^ private method1つ目は「関連関数 new は非公開」、2つ目は「メソッド add_interest は非公開」という意味です。defined here のラベルは「非公開の関連関数(メソッド)はここで定義されている」と、定義の場所を示しています。
このエラーが出るのは、次のプログラムです。
mod bank { pub struct Account { balance: u64, }
impl Account { fn new() -> Account { // no `pub` Account { balance: 0 } }
fn add_interest(&mut self) { // no `pub` self.balance += self.balance / 100; } }}
fn main() { let mut account = bank::Account::new(); // ❌ private associated function account.add_interest(); // ❌ private method}rustc はここで2つの呼び名を使っています。メソッド(method)は self を受け取るもので、account.add_interest() のように呼びます。関連関数(associated function)は self を受け取らないもので、Account::new() のように :: で呼びます。ルールも直し方も、どちらも同じです。
エラーの意味
Section titled “エラーの意味”どちらのエラーにも矢印が2つあります。^^^ の矢印は、あなたが呼んだ場所です。--- の矢印は、pub が足りない fn の行を示しています。つまりこのメッセージは、pub をどこに付ければいいかをぴったり教えてくれています。
impl Account の中にあるからといって、メソッドが公開になるわけではありません。Account が pub であっても同じです。レストランを思い浮かべてみてください。メニューは公開されているので、料理(pub のメソッド)は注文できます。でも厨房には「玉ねぎを刻む」のような厨房だけの作業があって、それを直接頼むことはできません。それが非公開のメソッドです。
Rust がこうする理由
Section titled “Rust がこうする理由”Rust では何もしなければすべてが非公開で、メソッドも例外ではありません(モジュールを見てください)。公開・非公開を決めるのは、impl ブロックが書かれているモジュールです。mod bank の中のコードなら add_interest を呼べますが、外のコードは呼べません。
こうすると、型は「正しい順番で使ったときだけ意味のある」小さなヘルパーメソッドを持てます。たとえば add_interest は、手数料を引いたあと、月に1回だけ実行されるべきだとします。誰でも呼べたら、うっかり2回呼んでしまう人が出てきます。非公開のヘルパーと、それを正しく実行する公開のメソッド1つを組み合わせれば、そんな間違いは起こりえません。
選択肢を残しておけるという利点もあります。非公開のメソッドは、いつでも名前を変えたり、まとめたり、消したりできます。外のコードがそれを使っていないことを、コンパイラが保証してくれるからです。
直し方1:メソッドに pub を付ける
Section titled “直し方1:メソッドに pub を付ける”そのメソッドを外から呼ぶつもりなら、これを選びます。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:自分のために用意された公開のメソッドを呼ぶ
Section titled “直し方2:自分のために用意された公開のメソッドを呼ぶ”非公開のメソッドがヘルパーのときは、これを選びます。自分で書いていないコードなら、なおさらです。多くの場合、そのヘルパーを正しい順番で、正しいチェックとともに呼んでくれる公開のメソッドがあります。次の例では、add_interest を非公開にしておくのが正しい設計です。
mod bank { pub struct Account { balance: u64, }
impl Account { pub fn new(opening: u64) -> Account { Account { balance: opening } }
// 公開の入り口:1回呼ぶだけで、1か月分の処理を正しい順番で行う。 pub fn close_month(&mut self) { self.charge_fee(); self.add_interest(); }
pub fn balance(&self) -> u64 { self.balance }
// 非公開のヘルパー:単独で呼んだり、2回呼んだりすると結果がおかしくなる。 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!("1か月後: {}", account.balance()); // account.add_interest(); // ❌ E0624: method `add_interest` is private}実行結果:
1か月後: 1004目安はこうです。いつ、単独で呼んでも構造体を壊さないメソッドだけを pub にしましょう。もっと大きな処理の1ステップとしてしか意味がないなら、非公開のままにして、その大きな処理のほうを公開します。
直し方3:「自分のコードだけ」なら pub(crate) を使う
Section titled “直し方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);}実行結果:
合計: 81つ注意があります。src/lib.rs と src/main.rs の両方があるパッケージでは、この2つは別々のクレートです。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 は非公開」という意味です。この場合は、ふつうの pub を使いましょう。Cargoとクレートも見てください。
直し方4:それが必要なコードをモジュールの中に移す
Section titled “直し方4:それが必要なコードをモジュールの中に移す”メソッドは本当に非公開のままにしておくべきだけれど、監査のチェックやテストのように、どうしてもそれが必要なコードがあるときは、これを選びます。同じモジュールの中のコードからは呼べますし、子モジュールからも呼べます。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()); // 非公開だが、ここでは呼べる }}よく出会う場面
Section titled “よく出会う場面”別のモジュールにある impl ブロック。 公開・非公開は、構造体ではなく impl が書かれているモジュールで決まります。Order が mod model にあっても、impl Order { fn with_tax(&self) ... } を mod pricing の中に書いたなら、main では method `with_tax` is private(メソッド with_tax は非公開)になり、エラーは 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 のそのクレートのページで、同じことができる公開のメソッドを探しましょう。docs.rs に載っているのは pub のアイテムだけです。
関連するエラー
Section titled “関連するエラー”- E0603:
function `x` is private(関数xは非公開)。ふつうの関数、構造体、モジュールについての同じ考え方です。 - E0616:
field `x` of struct `Y` is private(構造体Yのフィールドxは非公開)。フィールドを読み書きするときに出ます。 - E0451:
Y { ... }で構造体を作るときの非公開のフィールド。たいていはpub fn newで直します。 - E0599:
no method named `x` found(xという名前のメソッドが見つからない)。その型にメソッドがないか、トレイトがスコープにない。 - コンパイルエラーの一覧:エラー解説ページの全リストです。
理解度チェック
Section titled “理解度チェック”理解度チェック
1. mod clock の中に pub struct Timer と impl Timer { fn start() -> Timer { ... } } があります。main が clock::Timer::start() を呼ぶとどうなりますか?
2. mod bank で定義された非公開のメソッドを呼べるのは、どのコードですか?
3. 非公開の add_interest は、手数料を引いたあと、月に1回だけ実行されなければなりません。E0624 のいちばんよい直し方はどれですか?
スコア: 0 / 3
さらに詳しく
Section titled “さらに詳しく”- E0624 — Rust error codes index(英語):このエラーの公式の説明です。最小限の例が載っています。
- メソッド記法 — The Rust Programming Language 日本語版:メソッド、関連関数、
implブロックを説明している章です。 - モジュールツリーの要素を示すためのパス — The Rust Programming Language 日本語版:
pubでアイテムを公開する方法を、一歩ずつ説明しています。 - 非公開関数をテストする — The Rust Programming Language 日本語版:単体テストから非公開のコードを呼べる理由です。
- Visibility and privacy — The Rust Reference(英語):
pub(crate)、pub(super)、pub(in path)を含む、正確なルールです。