Menu
Coddy logo textTech

Setters

Parte de la sección Object Oriented Programming del Journey de Rust de Coddy — lección 12 de 61.

Los getters permiten que el código externo lea datos privados. ¿Pero qué pasa si necesitas permitirles cambiarlo? Ahí es donde entran los métodos setter: métodos públicos que modifican campos privados de una manera controlada.

Un setter básico toma &mut self y un nuevo valor:

// 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;
    }
}

A diferencia de los getters, los setters en Rust suelen utilizar el prefijo set_ para indicar claramente que modifican datos. El parámetro &mut self es esencial: otorga al método permiso para cambiar los campos de la estructura.

El verdadero poder de los setters es la validación. En lugar de aceptar ciegamente cualquier valor, puedes imponer reglas:

pub fn set_balance(&mut self, amount: f64) {
    if amount >= 0.0 {
        self.balance = amount;
    }
    // Los valores negativos se ignoran silenciosamente
}

Ahora la billetera se protege a sí misma: sin importar qué valor intente establecer el código externo, el saldo nunca puede ser negativo. Esto es la encapsulación en acción: el struct controla sus propios invariantes y el código externo simplemente no puede ponerlo en un estado no válido.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de termostato que demuestre cómo los setters pueden aplicar reglas para mantener la validez de los datos. Tu termostato tendrá un ajuste de temperatura que solo se puede ajustar dentro de un rango seguro.

Crearás dos archivos para organizar tu código:

  • thermostat.rs: Define una estructura Thermostat con un campo privado temperature (i32). La estructura debe ser pública, pero el campo permanece privado para protegerlo de valores inválidos. Implementa:
    • Un constructor new que reciba una temperatura inicial
    • Un getter temperature que devuelva la temperatura actual
    • Un setter set_temperature que solo acepte valores entre 10 y 30 (inclusive). Si el valor está fuera de este rango, la temperatura debe permanecer sin cambios.
  • main.rs: Importa tu módulo de termostato, crea un termostato y demuestra la validación del setter intentando establecer varias temperaturas y imprimiendo el resultado después de cada intento.

El setter debe ignorar silenciosamente los valores inválidos; sin mensajes de error, simplemente mantén la temperatura actual sin cambios cuando alguien intente establecerla fuera del rango válido.

Tu salida debe seguir este formato exacto:

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

Por ejemplo, si creas un termostato a 20, luego intentas establecerlo a 25 (válido) y después a 50 (inválido), la salida sería:

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

Recibirás tres entradas: la temperatura inicial, la primera temperatura a intentar y la segunda temperatura a intentar.

Hoja de referencia

Los métodos setter permiten que el código externo modifique campos privados de manera controlada. Toman &mut self para obtener permiso para cambiar los datos de la estructura.

Sintaxis básica de un setter:

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

Los setters en Rust suelen utilizar el prefijo set_ para indicar claramente que modifican datos.

Los setters permiten la validación para aplicar reglas y mantener la integridad de los datos:

pub fn set_balance(&mut self, amount: f64) {
    if amount >= 0.0 {
        self.balance = amount;
    }
    // Los valores no válidos se ignoran silenciosamente
}

Esto es la encapsulación en acción: la estructura controla sus propios invariantes, evitando que el código externo la ponga en un estado no válido.

Pruébalo tú mismo

mod thermostat;

use thermostat::Thermostat;

fn main() {
    // Leer las entradas
    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: Crear un termostato con la temperatura inicial

    // TODO: Imprimir la temperatura inicial
    // Formato: "Initial: {temperature}"

    // TODO: Intentar establecer la primera temperatura e imprimir el resultado
    // Formato: "After setting to {attempted_value}: {temperature}"

    // TODO: Intentar establecer la segunda temperatura e imprimir el resultado
    // Formato: "After setting to {attempted_value}: {temperature}"
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Object Oriented Programming