Menu
Coddy logo textTech

Private Fields

Part of the Object Oriented Programming section of Coddy's Rust journey — lesson 10 of 61.

In the previous lesson, you made everything public—the struct, its fields, and its methods. But true encapsulation means controlling how your data can be accessed. In Rust, you can make a struct public while keeping its fields private.

Consider a Wallet that should never have a negative balance. If the field is public, anyone could set it to any value:

// wallet.rs
pub struct Wallet {
    balance: f64,  // No pub = private!
}

impl Wallet {
    pub fn new(initial: f64) -> Wallet {
        Wallet { balance: initial }
    }
}

Notice that Wallet is public, but balance has no pub keyword. This means code outside the module can create a Wallet using the constructor, but cannot directly read or modify the balance field:

// main.rs
mod wallet;
use wallet::Wallet;

fn main() {
    let w = Wallet::new(100.0);
    // println!("{}", w.balance);  // Error! field is private
    // w.balance = -50.0;          // Error! can't access private field
}

This is the foundation of encapsulation—the struct controls its own data. External code must go through the methods you provide, which means you can add validation, logging, or any other logic. The data stays protected from invalid states.

In the upcoming lessons, you'll learn how to provide controlled access to this private data through getter and setter methods.

challenge icon

Challenge

Easy

Let's build a temperature sensor system that demonstrates encapsulation by keeping sensitive data protected inside a module.

You'll create two files to organize your code:

  • sensor.rs: Define a TemperatureSensor struct with two fields: location (String) and reading (f64). The struct should be public so it can be used from other files, but keep both fields private—external code shouldn't be able to directly access or modify the sensor's data. Add a public new constructor that takes a location name and an initial temperature reading.
  • main.rs: Bring in your sensor module and create a TemperatureSensor using the constructor. Since the fields are private, you won't be able to print them directly—instead, print a confirmation message showing that the sensor was created successfully.

This challenge focuses on the core concept of private fields: the struct is accessible, but its internal data is protected. External code can only interact with the sensor through the methods you provide (in this case, just the constructor).

Your output should follow this exact format:

Sensor created for: {location}

To access the location for printing, you'll need to have your new function print this message before returning the sensor, since the field itself is private.

For example, if you create a sensor with location "Kitchen" and reading 22.5, the output would be:

Sensor created for: Kitchen

You will receive two inputs: the location name and the initial temperature reading.

Cheat sheet

In Rust, you can make a struct public while keeping its fields private to achieve encapsulation. This prevents external code from directly accessing or modifying the internal data.

To make a field private, simply omit the pub keyword:

pub struct Wallet {
    balance: f64,  // No pub = private field
}

impl Wallet {
    pub fn new(initial: f64) -> Wallet {
        Wallet { balance: initial }
    }
}

In this example, Wallet is public (can be used outside the module), but balance is private (cannot be accessed directly from outside).

External code can create instances using public constructors but cannot read or modify private fields:

mod wallet;
use wallet::Wallet;

fn main() {
    let w = Wallet::new(100.0);
    // println!("{}", w.balance);  // Error! field is private
    // w.balance = -50.0;          // Error! can't access private field
}

This encapsulation ensures that the struct controls its own data, and external code must use the methods you provide to interact with it.

Try it yourself

mod sensor;

use sensor::TemperatureSensor;

fn main() {
    // Read input
    let mut location = String::new();
    std::io::stdin().read_line(&mut location).expect("Failed to read line");
    let location = location.trim().to_string();
    
    let mut reading_str = String::new();
    std::io::stdin().read_line(&mut reading_str).expect("Failed to read line");
    let reading: f64 = reading_str.trim().parse().expect("Failed to parse reading");
    
    // TODO: Create a TemperatureSensor using the constructor
    // The new function will handle printing the confirmation message
    
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming