Skip to content

Rust Error E0451: Field of Struct Is Private (Struct Literal)

E0451 means: you tried to build a struct with a literal like Account { ... }, but one of its fields is private. To write a struct literal you have to fill in every field, so one private field is enough to lock you out. That’s on purpose: the module wants you to go through its own constructor. The fix is almost always to call (or add) a new function. This page is part of the compiler errors guide.

error[E0451]: field `balance` of struct `Account` is private
--> src/main.rs:21:9
|
19 | let account = bank::Account {
| ------------- in this type
20 | owner: String::from("Ferris"),
21 | balance: 1_000_000, // ❌ private field
| ^^^^^^^ private field

This is the program that produces it:

mod bank {
pub struct Account {
pub owner: String,
balance: i64, // private
}
impl Account {
pub fn new(owner: &str) -> Account {
Account { owner: owner.to_string(), balance: 0 }
}
pub fn balance(&self) -> i64 {
self.balance
}
}
}
fn main() {
let account = bank::Account {
owner: String::from("Ferris"),
balance: 1_000_000, // ❌ private field
};
println!("{} has {}", account.owner, account.balance());
}

You may see one of its close cousins instead:

What you wrote What rustc says
the private field in the literal error[E0451]: field `balance` of struct `Account` is private
Config { verbose: true, ..Default::default() } E0451 too, pointing at ..Default::default() (see Fix 3)
the literal without the private field cannot construct `Account` with struct literal syntax due to private fields, often with help: you might have meant to use the `new` associated function
Percent(150) for a tuple struct E0603: tuple struct constructor `Percent` is private

All of them have the same fixes.

The arrow points at the field you’re not allowed to set: balance. owner is pub, so setting it is fine. But a struct literal is all or nothing. You can’t leave balance out (Rust doesn’t fill in missing fields), and you can’t set it either. So outside mod bank, there is simply no way to write Account { ... }.

Picture opening a real bank account. You don’t get to write your own starting balance on the form. You fill in your name, the bank creates the account, and the bank decides it starts at zero. Account::new is that form.

Struct fields are private by default, just like functions and modules (see Modules). If outside code could build an Account with any balance it likes, the module’s rules would mean nothing. Someone could create an account with a million, or with minus fifty. Because only mod bank can write the literal, every Account in the program is created by code that knows the rules.

There’s a second benefit. If you later add a private field, like created_at or history, code outside the module doesn’t break, because it never wrote the literal. It calls new, and you update new in one place.

Fix 1: Use (or add) a pub fn new constructor

Section titled “Fix 1: Use (or add) a pub fn new constructor”

Choose this most of the time. Look for a new (or from_..., with_...) function first. rustc often points you to it. If it’s your own struct and there’s none, write one:

mod bank {
pub struct Account {
pub owner: String,
balance: i64, // private
}
impl Account {
// Every account starts at 0. Money only comes in through deposit().
pub fn new(owner: &str) -> Account {
Account { owner: owner.to_string(), balance: 0 }
}
pub fn deposit(&mut self, amount: i64) {
if amount > 0 {
self.balance += amount;
}
}
pub fn balance(&self) -> i64 {
self.balance
}
}
}
fn main() {
let mut account = bank::Account::new("Ferris");
account.deposit(250);
println!("{} has {}", account.owner, account.balance());
}

Output:

Ferris has 250
▶ Try it Yourself

Inside mod bank the literal is allowed, so new can write Account { ... } freely. Don’t forget the pub on new itself; without it you get E0624 (associated function `new` is private). If some inputs should be refused, let new return Option<Account> or Result<Account, String>; the E0603 page shows one. More on constructors in Structs.

Fix 2: Make the fields pub when the data is plain

Section titled “Fix 2: Make the fields pub when the data is plain”

Choose this when any combination of values is fine. A colour is just three numbers; there’s no “wrong” colour to protect against. Then a literal is the clearest way to build it:

mod theme {
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
}
}
fn main() {
let ferris_orange = theme::Color { r: 247, g: 76, b: 0 };
println!("#{:02x}{:02x}{:02x}", ferris_orange.r, ferris_orange.g, ferris_orange.b);
}

Output:

#f74c00
▶ Try it Yourself

You need pub on every field. If only your own crate should build it, pub(crate) on the fields works too.

When to keep the field private instead: when a value could be wrong (a negative balance, a percentage above 100, an email without @), or when two fields must agree with each other (a total that must equal the sum of items). Then a literal would let people skip the check, and Fix 1 is the right call.

Fix 3: Start from Default, then change the public fields

Section titled “Fix 3: Start from Default, then change the public fields”

Choose this for “settings” structs with sensible defaults, where some fields are public and some are not. Here’s the trap first. The struct update syntax ..Default::default() looks like it should work, but it doesn’t:

mod settings {
pub struct Config {
pub verbose: bool,
retries: u32, // private: must stay between 1 and 10
}
impl Default for Config {
fn default() -> Config {
Config { verbose: false, retries: 3 }
}
}
}
fn main() {
let config = settings::Config { verbose: true, ..Default::default() };
println!("{}", config.verbose);
}
error[E0451]: field `retries` of struct `Config` is private
--> src/main.rs:15:54
|
15 | let config = settings::Config { verbose: true, ..Default::default() };
| ^^^^^^^^^^^^^^^^^^ field `retries` is private

..Default::default() is still a struct literal: it copies retries into the new struct, and that counts as setting it. Instead, create the default value first and change the public fields afterwards:

mod settings {
pub struct Config {
pub verbose: bool,
retries: u32, // private: must stay between 1 and 10
}
impl Default for Config {
fn default() -> Config {
Config { verbose: false, retries: 3 }
}
}
impl Config {
pub fn retries(&self) -> u32 {
self.retries
}
}
}
fn main() {
let mut config = settings::Config::default();
config.verbose = true; // public fields can be changed afterwards
println!("verbose: {}, retries: {}", config.verbose, config.retries());
}

Output:

verbose: true, retries: 3
▶ Try it Yourself

If every field is pub, ..Default::default() works fine. It’s only private fields that block it.

Choose this when there are several options and some of them have rules. Each method takes the struct, changes one setting (checking it), and hands the struct back, so the calls chain nicely:

mod settings {
pub struct Config {
verbose: bool,
retries: u32,
}
impl Config {
pub fn new() -> Config {
Config { verbose: false, retries: 3 }
}
pub fn verbose(mut self, on: bool) -> Config {
self.verbose = on;
self
}
// The rule lives here: 1 to 10 retries.
pub fn retries(mut self, n: u32) -> Config {
self.retries = n.clamp(1, 10);
self
}
pub fn describe(&self) -> String {
format!("verbose: {}, retries: {}", self.verbose, self.retries)
}
}
}
fn main() {
let config = settings::Config::new().verbose(true).retries(50);
println!("{}", config.describe());
}

Output:

verbose: true, retries: 10
▶ Try it Yourself

Asking for 50 retries quietly became 10, because the rule lives in one place. Many crates do this, sometimes with a separate ConfigBuilder type and a final .build() call. See Design patterns for that version.

After moving a struct into a module. Every User { name, email } in main.rs worked until User moved into mod user (or src/user.rs). Now they all fail. Decide per struct: plain data gets pub fields (Fix 2); anything with rules gets a pub fn new (Fix 1).

Types from the standard library or a crate. You can’t write Vec { ... } or Duration { ... }. rustc says cannot construct `Vec<_, _>` with struct literal syntax due to private fields and lists functions like Vec::new() and Vec::with_capacity(_). Use those, or Duration::from_secs(5).

A #[non_exhaustive] struct from another crate. Here even public fields don’t help, and the code is different: error[E0639]: cannot create non-exhaustive struct using struct expression. The crate wants to add fields later without breaking you, so use its constructor or Default, exactly like Fix 1 and Fix 3.

  • E0616: the same private field, but while reading it (account.balance).
  • E0603: tuple struct constructor `X` is private, and private functions, structs or modules.
  • E0624: associated function `new` is private. Your constructor needs pub too.
  • E0560: struct `X` has no field named `y` . A typo in the field name of a literal.
  • E0063: missing field `x` in initializer of `Y` . You left out a public field.
  • All compiler errors: the full list of error pages.

Quick check

1. pub struct Ticket { pub title: String, id: u32 } is in mod tickets. How can main create a Ticket?

2. Why does Config { verbose: true, ..Default::default() } fail when Config has a private field?

3. Which struct is a good fit for all-pub fields and plain literals?

Score: 0 / 3