コンテンツにスキップ

Rustのエラー E0603: function, struct or module is private(非公開の要素)

E0603 は、じつはよい知らせでもあります。Rust は、指定された関数や構造体、モジュールを見つけています。ただ、それが非公開(private)なので、渡すことが許されていないだけです。Rust のモジュールの中のものは、こちらが指定しない限りすべて非公開です。そのため、コードを初めてモジュールに分けたとき、ほとんどの人がこのエラーに出会います。直し方はたいてい、pub という1語だけです。このシリーズのほかのエラーは、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` がない
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

「function apply_discount is private」は「関数 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}");
}

1行目にはアイテムの種類が書かれるので、同じエラーがいくつかの形で表示されます。

1行目に出る文言 よくある原因 直し方
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

2つの矢印(-->)を読んでみましょう。1つ目(8行目)は、アイテムを使っている場所です。note: の矢印(2行目)は、アイテムが定義されている場所です。Rust はこう言っています。「あそこに apply_discount があるのは見えている。でも、共有するという印が付いていないので、billing の外のコードからは呼べない」。

ファイルが分かれているかどうかは関係ありません。billing が src/billing.rs にあっても同じエラーになり、note: が src/billing.rs:1:1 を指すだけです。

オフィスビルを思い浮かべてみてください。それぞれのモジュールは、ドアの付いた部屋です。非公開のアイテムは、部屋の中に置いたままの道具です。部屋の中にいる人(と、その中にある小部屋の人)は使えますが、廊下にいる人は誰も使えません。pub は、その道具を廊下の棚に出しておくことです。

Rust では、関数、構造体、構造体のフィールド、モジュール、そして use のインポートまで、すべてがデフォルトで非公開です。共有したいものに、自分で pub を付けます。モジュールのレッスンでも、最初にこのことを説明しています。

その理由は、未来の自分を守るためです。pub にしたものは、約束になります。ほかのコードがそれに頼るかもしれないので、変更するとそのコードが壊れるかもしれません。非公開のアイテムなら、変更も名前の変更も削除も自由で、外の誰もそれに頼っていないことをコンパイラが保証してくれます。小さなヘルパー関数はヘルパーのままでいられますし、構造体は自分のルール(「残高は決してマイナスにならない」など)を守れます。誰もフィールドを直接さわれないからです。

もう1つ大事な点があります。非公開かどうかは、パスの一歩一歩でチェックされます。shop::prices::price_of が使えるのは、prices が見えて、さらに price_of も見えるときだけです。非公開のモジュールの中にある pub fn には、やはり手が届きません。

直し方1:アイテムに(パスの途中のすべてのモジュールにも)pub を付ける

Section titled “直し方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 は2つ目のラベル function `price_of` is not publicly re-exported(関数 price_of は公開で再エクスポートされていない)でも教えてくれます。別ファイルのモジュールなら、宣言の行に pub を付けて pub mod prices; と書きます。

構造体の場合、pub struct Order で公開されるのは型だけです。外のコードがフィールドを読んだり書いたりするには、フィールドごとに pub が必要です。これについては直し方4で説明します。

直し方2:「自分のコードだけ」なら pub(crate) や pub(super) を使う

Section titled “直し方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: 3
pear: 5
▶ 実際に試してみよう

最後の行の // を消すと、pub(super) と書いてあるのに function `line` is private になります。main は receipt の親ではないので、ドアは閉じたままなのです。

直し方3:pub use で再エクスポートする

Section titled “直し方3:pub use で再エクスポートする”

内側のモジュールは内部の詳細にしておきたいけれど、その中の1つのアイテムには簡単にアクセスできるようにしたいときは、これを選びます。モジュールは非公開のまま、アイテムを短い公開のパスに置きます。

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
なしの値段: 5
▶ 実際に試してみよう

これは function import `price_of` is private(関数のインポート price_of は非公開)の直し方でもあります。shop の中のふつうの use prices::price_of; は、shop 専用のショートカットです。ほかのものと同じように、use も非公開なのです。pub use にすると、そのショートカットが公開のドアになります。大きなクレートはいつもこうしているので、たとえば serde::Serialize は、それがどの内側のモジュールにあるのかを知らなくても書けます。

直し方4:フィールドを公開する代わりに pub fn new を追加する

Section titled “直し方4:フィールドを公開する代わりに pub fn new を追加する”

エラーが構造体のフィールドやコンストラクタについてのときは、これを選びます。ここでは構造体は pub ですが、フィールドは 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%引き: 180
150%は使える? false
▶ 実際に試してみよう

これで、プログラム全体のどの Percent も、0から100の間にあることが保証されます。apply がアンダーフローすることも決してありません。new そのものにも pub が必要なことを忘れないでください。非公開のメソッドを呼ぶと、別のエラー E0624(associated function `new` is private、関連関数 new は非公開)になります。コンストラクタについて詳しくは構造体を見てください。

main.rs と lib.rs の両方があるパッケージ。 この2つは別々のクレートで、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 private
error[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 のそのクレートのページで、同じことができる公開の方法を探しましょう。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