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
EasyLet'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 aThermostatstruct with a privatetemperaturefield (i32). The struct should be public, but the field stays private to protect it from invalid values. Implement:- A
newconstructor that takes an initial temperature - A
temperaturegetter that returns the current temperature - A
set_temperaturesetter that only accepts values between 10 and 30 (inclusive). If the value is outside this range, the temperature should remain unchanged.
- A
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: 25You 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}"
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Methods and Behavior
Intro Implementation BlocksThe Self ParameterMutable MethodsAssociated FunctionsMultiple Implementation BlocksMethod ChainingRecap - Rectangle Actions4Project: Virtual Pet
Defining the PetFeeding the Pet7Standard Traits
The Debug TraitThe Display TraitClone and CopyEquality TraitsRecap - Printable Point10Project: Document System
The Draw TraitText Component2Encapsulation and Modules
Modules BasicsThe Public KeywordPrivate FieldsGettersSettersRecap - Secure Locker5Generics
Generic StructsGeneric MethodsMultiple Generic TypesGeneric FunctionsRecap - Coordinate Point8Traits as Bounds
Trait Bounds SyntaxMultiple BoundsThe Where ClauseReturning Types with TraitsRecap - Generic Printer11Design Patterns in Rust
Newtype PatternCompositionThe Drop TraitFrom and IntoRecap - Smart Pointer Mock