Skip to content

Rust Error E0603: Function, Struct or Module Is Private

E0603 is good news in disguise: Rust found the function, struct or module you asked for. It just isn’t allowed to hand it to you, because the item is private. Everything in a Rust module is private until you say otherwise, so this error shows up the first time almost everyone splits code into modules. The fix is usually one word, pub. This page is part of the compiler errors guide.

error[E0603]: function `apply_discount` is private
--> src/main.rs:8:26
|
8 | let total = billing::apply_discount(200); // ❌ private
| ^^^^^^^^^^^^^^ private function
|
note: the function `apply_discount` is defined here
--> src/main.rs:2:5
|
2 | fn apply_discount(price: u32) -> u32 { // no `pub`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

This is the program that produces it:

mod billing {
fn apply_discount(price: u32) -> u32 { // no `pub`
price * 90 / 100
}
}
fn main() {
let total = billing::apply_discount(200); // ❌ private
println!("Total: {total}");
}

The first line names the kind of item, so the same error comes in a few flavours:

What the first line says What usually went wrong Fix
function `apply_discount` is private the fn has no pub Fix 1 or Fix 2
struct `Order` is private the struct has no pub Fix 1
module `prices` is private a mod on the path has no pub Fix 1 or Fix 3
function import `price_of` is private you reached an item through a plain use inside another module Fix 3
tuple struct constructor `Percent` is private the struct is pub, but its fields are not Fix 4

Read the two arrows. The first one (line 8) is where you use the item. The note: arrow (line 2) is where the item is defined. Rust is saying: “I see apply_discount over there, but it was never marked as shared, so code outside billing can’t call it.”

The file doesn’t matter. If billing lives in src/billing.rs, you get the same error, and the note: simply points at src/billing.rs:1:1 instead.

Think of an office building. Each module is a room with a door. A private item is a tool that stays in its room: people inside the room (and in smaller rooms inside it) can use it, but nobody from the hallway can. pub puts the tool on a shelf in the hallway.

Rust makes everything private by default: functions, structs, struct fields, modules and even use imports. You opt in to sharing with pub. The Modules lesson shows this from the start.

The reason is safety for future you. Whatever is pub becomes a promise: other code may depend on it, so changing it can break that code. Private items are free to change, rename or delete, and the compiler guarantees nobody outside relies on them. Small helper functions stay helpers, and a struct can protect its own rules (like “a balance is never negative”) because nobody else can touch its fields directly.

One more detail: privacy is checked for every step of a path. shop::prices::price_of only works if prices is visible to you and price_of is visible to you. A pub fn inside a private module is still out of reach.

Fix 1: Add pub to the item (and to every module on the path)

Section titled “Fix 1: Add pub to the item (and to every module on the path)”

Choose this when the item is meant to be used from outside its module. This is the right fix most of the time:

mod billing {
pub fn apply_discount(price: u32) -> u32 {
price * 90 / 100
}
}
fn main() {
let total = billing::apply_discount(200);
println!("Total: {total}");
}

Output:

Total: 180
▶ Try it Yourself

If the error says module `prices` is private, the missing pub is on the module: write pub mod prices { ... }. rustc also tells you this case with a second label, function `price_of` is not publicly re-exported. With a separate file, the pub goes on the declaration line: pub mod prices;.

For a struct, pub struct Order makes the type public, but each field still needs its own pub if outside code should read or set it. More on that in Fix 4.

Fix 2: Use pub(crate) or pub(super) for “only my code”

Section titled “Fix 2: Use pub(crate) or pub(super) for “only my code””

Choose this when the item should be shared, but not with the whole world. pub has a few levels, from smallest to biggest:

You write Who can use the item
nothing (private) its own module, and the modules inside it
pub(super) also the parent module
pub(crate) any code in this crate
pub everyone, including other crates that depend on yours

In a program (a binary), pub(crate) and pub behave almost the same. The difference matters in a library: pub(crate) lets your modules share a helper without making it part of the library’s public API.

mod shop {
// Any code in this crate may call this.
pub(crate) fn store_name() -> &'static str {
"Ferris Fruits"
}
pub mod receipt {
// Only the parent module, `shop`, may call this helper.
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!("Welcome to {}", shop::store_name());
shop::print_receipt(&[("apple", 3), ("pear", 5)]);
// shop::receipt::line("apple", 3); // ❌ E0603: `line` is only for `shop`
}

Output:

Welcome to Ferris Fruits
== Ferris Fruits ==
apple: 3
pear: 5
▶ Try it Yourself

Remove the // in front of the last line and you get function `line` is private, even though it says pub(super). main is not the parent of receipt, so the door stays closed.

Choose this when the inner module is an internal detail, but one item inside it should be easy to reach. Keep the module private and put the item on a shorter, public path:

mod shop {
mod prices {
// stays private: how prices are stored is shop's business
pub fn price_of(item: &str) -> u32 {
if item == "apple" { 3 } else { 5 }
}
}
pub use prices::price_of; // now reachable as shop::price_of
}
fn main() {
println!("Apples cost {}", shop::price_of("apple"));
println!("Pears cost {}", shop::price_of("pear"));
}

Output:

Apples cost 3
Pears cost 5
▶ Try it Yourself

This is also the fix for function import `price_of` is private. A plain use prices::price_of; inside shop is a shortcut for shop only; like everything else, a use is private. pub use turns the shortcut into a public door. Big crates do this all the time, so you can write serde::Serialize without knowing which inner module it lives in.

Fix 4: Add a pub fn new instead of making fields public

Section titled “Fix 4: Add a pub fn new instead of making fields public”

Choose this when the error is about a struct’s fields or its constructor. Here the struct is pub, but its field isn’t:

mod billing {
pub struct Percent(u8);
}
fn main() {
let discount = billing::Percent(150); // ❌ private constructor
}
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 constructor
| ^^^^^^^ private tuple struct constructor
|
help: consider making the field publicly accessible
|
2 | pub struct Percent(pub u8);
| +++

rustc suggests pub u8, and for a plain data type that’s fine. But look at the value: 150 percent! Often the private field is the point. The module wants to check the value before a Percent exists. Give it a public constructor that does the checking:

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 with 10% off: {}", ten.apply(200));
println!("Is 150% allowed? {}", billing::Percent::new(150).is_some());
}

Output:

200 with 10% off: 180
Is 150% allowed? false
▶ Try it Yourself

Now every Percent in the whole program is guaranteed to be between 0 and 100, and apply can never underflow. Remember that new itself needs pub too; a private method gives a different error, E0624 (associated function `new` is private). See Structs for more on constructors.

A package with both main.rs and lib.rs. They are two separate crates, and main.rs uses the library from the outside. If lib.rs says mod inventory;, then use shop::inventory::count_items; in main.rs fails:

error[E0603]: module `inventory` is private
--> src/main.rs:1:11
|
1 | use shop::inventory::count_items;
| ^^^^^^^^^ private module

Fix: write pub mod inventory; in lib.rs and pub fn count_items in the module. Watch out: pub(crate) is not enough here. To the library, main.rs is another crate, so a pub(crate) fn still gives function `count_items` is private. See Cargo and crates.

Private struct fields. These get their own error codes. Creating a struct with a private field gives E0451, and reading one gives E0616:

error[E0451]: field `balance` of struct `Account` is private
error[E0616]: field `balance` of struct `Account` is private

Fix: the same choice as Fix 4. Add pub to the field if anyone may change it, or keep it private and add pub fn new(...) plus a getter like pub fn balance(&self) -> u64.

A function you found in a crate’s source code. You read a crate on GitHub, found the perfect helper, and get E0603 when you call it. If the crate’s authors didn’t make it pub, you can’t use it, and they may change it any time. Look for a public way to do the same thing on the crate’s page at docs.rs; only pub items appear there.

  • E0432: unresolved import. The item wasn’t found at all. With E0603 it was found, but it’s private.
  • E0433: failed to resolve, a path in your code that leads nowhere.
  • E0451 and E0616: a struct field is private (creating the struct, or reading the field).
  • E0624: a method or associated function (like new) is private.
  • All compiler errors: the full list of error pages.

Quick check

1. mod shop { mod prices { pub fn price_of() {} } } and main calls shop::prices::price_of(). Which error do you get?

2. A helper in src/lib.rs is marked pub(crate). Can src/main.rs in the same package call it?

3. A pub struct Email(String); keeps its field private on purpose. What's the friendly way to let other modules create one?

Score: 0 / 3