Сеттеры
Часть раздела Object Oriented Programming путешествия по Rust на Coddy — урок 12 из 61.
Геттеры позволяют внешнему коду читать приватные данные. Но что, если вам нужно позволить ему изменять их? Здесь на помощь приходят методы-сеттеры — публичные методы, которые изменяют приватные поля контролируемым образом.
Базовый сеттер принимает &mut self и новое значение:
// 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;
}
}
В отличие от геттеров, сеттеры в Rust обычно используют префикс set_, чтобы явно указать на изменение данных. Параметр &mut self необходим — он дает методу разрешение на изменение полей структуры.
Настоящая сила сеттеров заключается в валидации. Вместо того чтобы слепо принимать любое значение, вы можете применять правила:
pub fn set_balance(&mut self, amount: f64) {
if amount >= 0.0 {
self.balance = amount;
}
// Отрицательные значения молча игнорируются
}
Теперь кошелек защищает себя — какое бы значение ни пытался установить внешний код, баланс никогда не сможет стать отрицательным. Это инкапсуляция в действии: struct контролирует свои собственные инварианты, и внешний код просто не может привести её в недопустимое состояние.
Задание
ЛегкоДавайте создадим систему термостата, которая демонстрирует, как сеттеры могут применять правила для обеспечения валидности данных. Ваш термостат будет иметь настройку температуры, которую можно регулировать только в пределах безопасного диапазона.
Для организации кода вы создадите два файла:
thermostat.rs: Определите структуруThermostatс приватным полемtemperature(i32). Структура должна быть публичной, но поле остается приватным, чтобы защитить его от недопустимых значений. Реализуйте:- Конструктор
new, который принимает начальную температуру - Геттер
temperature, который возвращает текущую температуру - Сеттер
set_temperature, который принимает только значения от 10 до 30 (включительно). Если значение находится вне этого диапазона, температура должна остаться неизменной.
- Конструктор
main.rs: Подключите модуль термостата, создайте термостат и продемонстрируйте валидацию сеттера, пытаясь установить различные значения температуры и выводя результат после каждой попытки.
Сеттер должен молча игнорировать недопустимые значения — никаких сообщений об ошибках, просто сохраняйте текущую температуру без изменений, когда кто-то пытается установить её вне допустимого диапазона.
Ваш вывод должен соответствовать следующему формату:
Initial: {temperature}
After setting to {attempted_value}: {temperature}
After setting to {attempted_value}: {temperature}Например, если вы создадите термостат со значением 20, затем попытаетесь установить его на 25 (допустимо), а затем на 50 (недопустимо), вывод будет следующим:
Initial: 20
After setting to 25: 25
After setting to 50: 25Вы получите три входных значения: начальную температуру, первую попытку установки температуры и вторую попытку установки температуры.
Шпаргалка
Методы-сеттеры позволяют внешнему коду изменять приватные поля контролируемым образом. Они принимают &mut self, чтобы получить разрешение на изменение данных структуры.
Базовый синтаксис сеттера:
pub fn set_balance(&mut self, amount: f64) {
self.balance = amount;
}Сеттеры в Rust обычно используют префикс set_, чтобы четко указать, что они изменяют данные.
Сеттеры позволяют выполнять валидацию для обеспечения соблюдения правил и поддержания целостности данных:
pub fn set_balance(&mut self, amount: f64) {
if amount >= 0.0 {
self.balance = amount;
}
// Недопустимые значения молча игнорируются
}Это инкапсуляция в действии: структура контролирует свои собственные инварианты, не позволяя внешнему коду перевести её в недопустимое состояние.
Попробуйте сами
mod thermostat;
use thermostat::Thermostat;
fn main() {
// Чтение входных данных
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: Создайте термостат с начальной температурой
// TODO: Выведите начальную температуру
// Формат: "Initial: {temperature}"
// TODO: Попробуйте установить первую температуру и выведите результат
// Формат: "After setting to {attempted_value}: {temperature}"
// TODO: Попробуйте установить вторую температуру и выведите результат
// Формат: "After setting to {attempted_value}: {temperature}"
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Методы и поведение
Введение в блоки реализацииПараметр SelfИзменяемые методыАссоциированные функцииНесколько блоков реализацииЦепочки вызовов методовИтоги: действия с Rectangle4Проект: Виртуальный питомец
Определение питомцаКормление питомца2Инкапсуляция и модули
Основы модулейКлючевое слово PublicПриватные поляГеттерыСеттерыИтоги — Безопасный сейф5Обобщения
Обобщённые структурыОбобщённые методыНесколько обобщённых типовОбобщённые функцииПовторение — Координатная точка8Трейты как ограничения
Синтаксис ограничений трейтовМножественные ограниченияКонструкция whereВозврат типов с трейтамиИтоги — Обобщённый принтер11Паттерны проектирования в Rust
Паттерн NewtypeКомпозицияТрейт DropFrom и IntoИтоги — Мок-объект Smart Pointer3Продвинутые перечисления
Перечисления с даннымиМетоды в перечисленияхСопоставление вариантов с даннымиСнова об Option EnumИтоги — перечисление Shape6Определение трейтов
Что такое трейт?Реализация трейтовРеализации по умолчаниюПереопределение реализаций по умолчаниюТрейты с параметрамиИтоги — Media Player