コンテンツにスキップ

Rustのエラー E0624: method is private(非公開のメソッド)

E0624 は、呼んだメソッドはあるけれど、pub を付けずに書かれているので、そのモジュールの中のコードからしか呼べない という意味のエラーです。構造体が公開されていても、モジュールが公開されていても、メソッドは1つずつ、非公開から始まります。型を自分のモジュールに移した直後にとてもよく出会うエラーで、たいていは fn の前に pub を付ければ直ります。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。

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 method

1つ目は「関連関数 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() のように :: で呼びます。ルールも直し方も、どちらも同じです。

どちらのエラーにも矢印が2つあります。^^^ の矢印は、あなたが呼んだ場所です。--- の矢印は、pub が足りない fn の行を示しています。つまりこのメッセージは、pub をどこに付ければいいかをぴったり教えてくれています。

impl Account の中にあるからといって、メソッドが公開になるわけではありません。Account が pub であっても同じです。レストランを思い浮かべてみてください。メニューは公開されているので、料理(pub のメソッド)は注文できます。でも厨房には「玉ねぎを刻む」のような厨房だけの作業があって、それを直接頼むことはできません。それが非公開のメソッドです。

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);
}

実行結果:

合計: 8
▶ 実際に試してみよう

1つ注意があります。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()); // 非公開だが、ここでは呼べる
}
}

別のモジュールにある 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 のアイテムだけです。

  • 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 という名前のメソッドが見つからない)。その型にメソッドがないか、トレイトがスコープにない。
  • コンパイルエラーの一覧:エラー解説ページの全リストです。

理解度チェック

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