Setter
Fa parte della sezione Programmazione orientata agli oggetti del percorso Rust di Coddy. Lezione 12 di 61.
I getter permettono al codice esterno di leggere dati privati. Ma cosa succede se devi permettergli di modificarli? È qui che entrano in gioco i metodi setter: metodi pubblici che modificano i campi privati in modo controllato.
Un setter di base accetta &mut self e un nuovo valore:
// 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 differenza dei getter, i setter di Rust usano tipicamente il prefisso set_ per indicare chiaramente che modificano i dati. Il parametro &mut self è essenziale. Consente al metodo di modificare i campi della struct.
Il vero potere dei setter è la convalida. Invece di accettare ciecamente qualsiasi valore, puoi applicare delle regole:
pub fn set_balance(&mut self, amount: f64) {
if amount >= 0.0 {
self.balance = amount;
}
// I valori negativi vengono ignorati senza alcun avviso
}
Ora il portafoglio si protegge da solo: qualunque valore il codice esterno provi a impostare, il saldo non può mai diventare negativo. Questa è l’incapsulamento in azione: la struct controlla i propri invarianti e il codice esterno semplicemente non può portarla in uno stato non valido.
Sfida
FacileCostruiamo un sistema di termostato che dimostri come i setter possano applicare regole per mantenere validi i dati. Il tuo termostato avrà un'impostazione della temperatura che può essere regolata solo entro un intervallo sicuro.
Creerai due file per organizzare il codice:
thermostat.rs: definisci una structThermostatcon un campotemperatureprivato (i32). La struct dovrebbe essere pubblica, ma il campo rimane privato per proteggerlo da valori non validi. Implementa:- Un costruttore
newche accetta una temperatura iniziale - Un getter
temperatureche restituisce la temperatura corrente - Un setter
set_temperatureche accetta solo valori compresi tra 10 e 30 (inclusi). Se il valore è al di fuori di questo intervallo, la temperatura dovrebbe rimanere invariata.
- Un costruttore
main.rs: importa il modulo del termostato, crea un termostato e mostra la convalida del setter provando a impostare varie temperature e stampando il risultato dopo ogni tentativo.
Il setter dovrebbe ignorare silenziosamente i valori non validi: nessun messaggio di errore, semplicemente mantiene invariata la temperatura corrente quando qualcuno prova a impostarla al di fuori dell'intervallo valido.
L'output deve seguire esattamente questo formato:
Initial: {temperature}
After setting to {attempted_value}: {temperature}
After setting to {attempted_value}: {temperature}Ad esempio, se crei un termostato a 20, poi provi a impostarlo a 25 (valido) e poi a 50 (non valido), l'output sarà:
Initial: 20
After setting to 25: 25
After setting to 50: 25Riceverai tre input: la temperatura iniziale, la prima temperatura da provare e la seconda temperatura da provare.
Provalo tu
mod thermostat;
use thermostat::Thermostat;
fn main() {
// Lee 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: Crea un termostato con la temperatura inicial
// TODO: Imprime la temperatura inicial
// Formato: "Initial: {temperature}"
// TODO: Intenta establecer la primera temperatura e imprime el resultado
// Formato: "After setting to {attempted_value}: {temperature}"
// TODO: Intenta establecer la segunda temperatura e imprime el resultado
// Formato: "After setting to {attempted_value}: {temperature}"
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Metodi e comportamento
Introduzione ai blocchi implIl parametro selfMetodi mutabiliFunzioni associateBlocchi impl multipliConcatenamento di metodiRiepilogo - Azioni sul rettangolo4Progetto: animale domestico virtuale
Definire l'animaleNutrire l'animale7Trait standard
Il trait DebugIl trait DisplayClone e CopyTrait di uguaglianzaRiepilogo - Punto stampabile10Progetto: sistema di documenti
Il trait DrawComponente di testo2Incapsulamento e moduli
Nozioni di base sui moduliLa parola chiave publicCampi privatiGetterSetterRiepilogo - Armadietto sicuro5Generics
Struct genericheMetodi genericiPiù tipi genericiFunzioni genericheRipasso - Punto di coordinate8I Trait come vincoli
Sintassi dei vincoli sui traitVincoli multipliLa clausola whereRestituire tipi con i traitRiepilogo - Stampante generica11Modelli di progettazione in Rust
Pattern NewtypeComposizioneIl trait DropFrom e IntoRipasso - Mock con smart pointer3Enumerazioni avanzate
Enumerazioni con datiMetodi sulle enumerazioniCorrispondenza delle varianti con datiL'enumerazione Option ripresaRiepilogo - enumerazione Shape6Definizione dei trait
Che cos’è un trait?Implementare i traitImplementazioni predefiniteSovrascrivere le implementazioni predefiniteTrait con parametriRiepilogo - Lettore multimedialeEsercitati da solo: Compilatore Rust online