Menu
Coddy logo textTech

Setters

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

Getters let external code read private data. But what if you need to let them change it? That's where setter methods come in—public methods that modify private fields in a controlled way.

A basic setter takes &mut self and a new value:

// wallet.rs
pub struct Wallet {
    balance: f64,
}

impl Wallet {
    pub fn new(initial: f64) -> Wallet {
        Self { balance: initial }
    }
    
    pub fn balance(&self) -> f64 {
        self.balance
    }
    
    pub fn set_balance(&mut self, amount: f64) {
        self.balance = amount;
    }
}

Unlike getters, Rust setters typically use the set_ prefix to clearly indicate they modify data. The &mut self parameter is essential—it grants the method permission to change the struct's fields.

The real power of setters is validation. Instead of blindly accepting any value, you can enforce rules:

pub fn set_balance(&mut self, amount: f64) {
    if amount >= 0.0 {
        self.balance = amount;
    }
    // Negative values are silently ignored
}

Now the wallet protects itself—no matter what value external code tries to set, the balance can never go negative. This is encapsulation in action: the struct controls its own invariants, and external code simply cannot put it into an invalid state.

challenge icon

Challenge

Easy

Let's build a thermostat system that demonstrates how setters can enforce rules to keep data valid. Your thermostat will have a temperature setting that can only be adjusted within a safe range.

You'll create two files to organize your code:

  • thermostat.rs: Define a Thermostat struct with a private temperature field (i32). The struct should be public, but the field stays private to protect it from invalid values. Implement:
    • A new constructor that takes an initial temperature
    • A temperature getter that returns the current temperature
    • A set_temperature setter that only accepts values between 10 and 30 (inclusive). If the value is outside this range, the temperature should remain unchanged.
  • main.rs: Bring in your thermostat module, create a thermostat, and demonstrate the setter's validation by attempting to set various temperatures and printing the result after each attempt.

The setter should silently ignore invalid values—no error messages, just keep the current temperature unchanged when someone tries to set it outside the valid range.

Your output should follow this exact format:

Initial: {temperature}
After setting to {attempted_value}: {temperature}
After setting to {attempted_value}: {temperature}

For example, if you create a thermostat at 20, then try to set it to 25 (valid) and then 50 (invalid), the output would be:

Initial: 20
After setting to 25: 25
After setting to 50: 25

You will receive three inputs: the initial temperature, the first temperature to attempt, and the second temperature to attempt.

Cheat sheet

Setter methods allow external code to modify private fields in a controlled way. They take &mut self to gain permission to change the struct's data.

Basic setter syntax:

pub fn set_balance(&mut self, amount: f64) {
    self.balance = amount;
}

Rust setters typically use the set_ prefix to clearly indicate they modify data.

Setters enable validation to enforce rules and maintain data integrity:

pub fn set_balance(&mut self, amount: f64) {
    if amount >= 0.0 {
        self.balance = amount;
    }
    // Invalid values are silently ignored
}

This is encapsulation in action: the struct controls its own invariants, preventing external code from putting it into an invalid state.

Try it yourself

mod thermostat;

use thermostat::Thermostat;

fn main() {
    // Read inputs
    let mut input1 = String::new();
    std::io::stdin().read_line(&mut input1).expect("Failed to read line");
    let initial_temp: i32 = input1.trim().parse().expect("Invalid number");

    let mut input2 = String::new();
    std::io::stdin().read_line(&mut input2).expect("Failed to read line");
    let first_attempt: i32 = input2.trim().parse().expect("Invalid number");

    let mut input3 = String::new();
    std::io::stdin().read_line(&mut input3).expect("Failed to read line");
    let second_attempt: i32 = input3.trim().parse().expect("Invalid number");

    // TODO: Create a thermostat with the initial temperature

    // TODO: Print the initial temperature
    // Format: "Initial: {temperature}"

    // TODO: Attempt to set the first temperature and print the result
    // Format: "After setting to {attempted_value}: {temperature}"

    // TODO: Attempt to set the second temperature and print the result
    // Format: "After setting to {attempted_value}: {temperature}"
}
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