Setters
Parte da seção Object Oriented Programming do Journey de Rust da Coddy — lição 12 de 61.
Getters permitem que o código externo leia dados privados. Mas e se você precisar permitir que eles os alterem? É aí que entram os métodos setter—métodos públicos que modificam campos privados de uma maneira controlada.
Um setter básico recebe &mut self e um novo 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;
}
}
Ao contrário dos getters, os setters em Rust normalmente usam o prefixo set_ para indicar claramente que eles modificam dados. O parâmetro &mut self é essencial—ele concede ao método permissão para alterar os campos da struct.
O verdadeiro poder dos setters é a validação. Em vez de aceitar cegamente qualquer valor, você pode aplicar regras:
pub fn set_balance(&mut self, amount: f64) {
if amount >= 0.0 {
self.balance = amount;
}
// Valores negativos são ignorados silenciosamente
}
Agora a carteira se protege — não importa qual valor o código externo tente definir, o saldo nunca pode ficar negativo. Isso é encapsulamento em ação: a struct controla seus próprios invariantes, e o código externo simplesmente não pode colocá-la em um estado inválido.
Desafio
FácilVamos construir um sistema de termostato que demonstra como os setters podem aplicar regras para manter os dados válidos. Seu termostato terá uma configuração de temperatura que só pode ser ajustada dentro de uma faixa segura.
Você criará dois arquivos para organizar seu código:
thermostat.rs: Defina uma structThermostatcom um campo privadotemperature(i32). A struct deve ser pública, mas o campo permanece privado para protegê-lo de valores inválidos. Implemente:- Um construtor
newque recebe uma temperatura inicial - Um getter
temperatureque retorna a temperatura atual - Um setter
set_temperatureque aceita apenas valores entre 10 e 30 (inclusive). Se o valor estiver fora desta faixa, a temperatura deve permanecer inalterada.
- Um construtor
main.rs: Importe seu módulo de termostato, crie um termostato e demonstre a validação do setter tentando definir várias temperaturas e imprimindo o resultado após cada tentativa.
O setter deve ignorar silenciosamente valores inválidos — sem mensagens de erro, apenas mantenha a temperatura atual inalterada quando alguém tentar defini-la fora da faixa válida.
Sua saída deve seguir este formato exato:
Initial: {temperature}
After setting to {attempted_value}: {temperature}
After setting to {attempted_value}: {temperature}Por exemplo, se você criar um termostato em 20, depois tentar defini-lo como 25 (válido) e depois 50 (inválido), a saída seria:
Initial: 20
After setting to 25: 25
After setting to 50: 25Você receberá três entradas: a temperatura inicial, a primeira temperatura a tentar e a segunda temperatura a tentar.
Folha de consulta
Métodos setter permitem que código externo modifique campos privados de forma controlada. Eles recebem &mut self para obter permissão para alterar os dados da struct.
Sintaxe básica de um setter:
pub fn set_balance(&mut self, amount: f64) {
self.balance = amount;
}Setters em Rust normalmente usam o prefixo set_ para indicar claramente que modificam dados.
Setters permitem a validação para aplicar regras e manter a integridade dos dados:
pub fn set_balance(&mut self, amount: f64) {
if amount >= 0.0 {
self.balance = amount;
}
// Valores inválidos são ignorados silenciosamente
}Isso é o encapsulamento em ação: a struct controla suas próprias invariantes, impedindo que o código externo a coloque em um estado inválido.
Experimente você mesmo
mod thermostat;
use thermostat::Thermostat;
fn main() {
// Ler as 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: Criar um termostato com a temperatura inicial
// TODO: Imprimir a temperatura inicial
// Formato: "Initial: {temperature}"
// TODO: Tentar definir a primeira temperatura e imprimir o resultado
// Formato: "After setting to {attempted_value}: {temperature}"
// TODO: Tentar definir a segunda temperatura e imprimir o resultado
// Formato: "After setting to {attempted_value}: {temperature}"
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Object Oriented Programming
1Métodos e Comportamento
Introdução a Blocos de ImplementaçãoO Parâmetro SelfMétodos MutáveisFunções AssociadasMúltiplos Blocos de ImplementaçãoEncadeamento de MétodosRecapitulação - Ações de Retângulo4Projeto: Pet Virtual
Definindo o PetAlimentando o Pet7Traits Padrão
A Trait DebugA Trait DisplayClone e CopyTraits de IgualdadeRecapitulação - Ponto Imprimível10Projeto: Sistema de Documentos
A Trait DrawComponente de Texto2Encapsulamento e Módulos
Fundamentos de MódulosA Palavra-chave PublicCampos PrivadosGettersSettersRecapitulação - Cofre Seguro5Genéricos
Structs GenéricasMétodos GenéricosMúltiplos Tipos GenéricosFunções GenéricasRecapitulação - Ponto de Coordenada8Traits como Restrições
Sintaxe de Restrições de TraitRestrições MúltiplasA Cláusula WhereRetornando Tipos com TraitsRecapitulação - Impressora Genérica11Padrões de Projeto em Rust
Padrão NewtypeComposiçãoA Trait DropFrom e IntoRecapitulação - Mock de Smart Pointer3Enums Avançados
Enums com DadosMétodos em EnumsCorrespondência de Variantes de DadosO Enum Option RevisitadoRecapitulação - Enum Shape6Definição de Traits
O que é uma Trait?Implementando TraitsImplementações PadrãoSobrescrevendo PadrõesTraits com ParâmetrosRecapitulação - Media Player