A public struct can still keep fields private. Public methods become the supported way to build and read the value.

Program

Play the program to choose the retry count passed through a public constructor.

retries
private_fields.rs
Replay: real traced execution (multi-file project)
mod settings {
    pub struct Config {
        pub name: &'static str,
        retries: i32,
    }

    impl Config {
        pub fn new(name: &'static str, retries: i32) -> Self {
            Self { name, retries }
        }

        pub fn summary(&self) -> String {
            format!("{}:{}", self.name, self.retries)
        }
    }
}

fn main() {
    let retries = 2;
    let cfg = settings::Config::new("api", retries);
    let label = cfg.summary();
    println!("{label}");
}
mod settings {
    pub struct Config {
        pub name: &'static str,
        retries: i32,
    }

    impl Config {
        pub fn new(name: &'static str, retries: i32) -> Self {
            Self { name, retries }
        }

        pub fn summary(&self) -> String {
            format!("{}:{}", self.name, self.retries)
        }
    }
}

fn main() {
    let retries = 1;
    let cfg = settings::Config::new("api", retries);
    let label = cfg.summary();
    println!("{label}");
}
mod settings {
    pub struct Config {
        pub name: &'static str,
        retries: i32,
    }

    impl Config {
        pub fn new(name: &'static str, retries: i32) -> Self {
            Self { name, retries }
        }

        pub fn summary(&self) -> String {
            format!("{}:{}", self.name, self.retries)
        }
    }
}

fn main() {
    let retries = 4;
    let cfg = settings::Config::new("api", retries);
    let label = cfg.summary();
    println!("{label}");
}
  1. retries ← 2

    18fn main() {19    let retrie→ 2s = 2; //@retries=2, 1, 420    let cfg = settings::Config::new("api", retrie2s);21    let label = cfg.summary();
  2. cfg ← (empty)

    19let retries = 2; //@retries=2, 1, 420let cf→ (empty)g = settings::Config::new("api", retrie2s);21let label = cfg.summary();22println!("{label}");
  3. label ← "api:2"

    20    let cfg = settings::Config::new("api", retries);21    let labe→ "api:2"l = cfg.summary();22    println!("{label}");23}
    outputapi:2
  1. retries ← 1

    18fn main() {19    let retrie→ 1s = 1;20    let cfg = settings::Config::new("api", retrie1s);21    let label = cfg.summary();
  2. cfg ← (empty)

    19let retries = 1;20let cf→ (empty)g = settings::Config::new("api", retrie1s);21let label = cfg.summary();22println!("{label}");
  3. label ← "api:1"

    20    let cfg = settings::Config::new("api", retries);21    let labe→ "api:1"l = cfg.summary();22    println!("{label}");23}
    outputapi:1
  1. retries ← 4

    18fn main() {19    let retrie→ 4s = 4;20    let cfg = settings::Config::new("api", retrie4s);21    let label = cfg.summary();
  2. cfg ← (empty)

    19let retries = 4;20let cf→ (empty)g = settings::Config::new("api", retrie4s);21let label = cfg.summary();22println!("{label}");
  3. label ← "api:4"

    20    let cfg = settings::Config::new("api", retries);21    let labe→ "api:4"l = cfg.summary();22    println!("{label}");23}
    outputapi:4
private field `retries` has no `pub`, so callers cannot set it directly.
constructor `Config::new` is the public creation path.
method `summary` exposes a stable view without exposing every field.