Снова об Option Enum
Часть раздела Object Oriented Programming путешествия по Rust на Coddy — урок 17 из 61.
Вы уже встречали Option<T> в Rust — это стандартное перечисление для представления значений, которые могут существовать или отсутствовать. Теперь давайте посмотрим на него через призму ООП: Option — это не просто перечисление, это тип с мощными встроенными методами, которые позволяют безопасно и элегантно обрабатывать nullable-значения.
Вместо того чтобы каждый раз использовать сопоставление, вы можете использовать методы непосредственно для значений Option. Метод unwrap_or возвращает внутреннее значение, если оно существует, или значение по умолчанию, которое вы предоставите:
let maybe_name: Option<String> = Some(String::from("Alice"));
let name = maybe_name.unwrap_or(String::from("Guest"));
println!("{}", name); // Alice
let empty: Option<String> = None;
let fallback = empty.unwrap_or(String::from("Guest"));
println!("{}", fallback); // Guest
Метод map преобразует внутреннее значение, если оно существует, оставляя None без изменений. Это позволяет выстраивать цепочки операций без постоянной проверки на None:
let maybe_num: Option<i32> = Some(5);
let doubled = maybe_num.map(|x| x * 2);
println!("{:?}", doubled); // Some(10)
let nothing: Option<i32> = None;
let still_nothing = nothing.map(|x| x * 2);
println!("{:?}", still_nothing); // None
Эти методы воплощают принципы ООП — тип Option инкапсулирует концепцию «возможно, значение» и предоставляет чистый интерфейс для работы с ним. Вы вызываете методы объекта, а не пишете внешнюю логику для его проверки.
Задание
ЛегкоДавайте создадим систему профилей пользователей, которая изящно обрабатывает необязательные данные с помощью методов Option. У пользователей может быть установлен никнейм, а может и не быть — ваша система будет элегантно обрабатывать оба случая без явного сопоставления с образцом (matching).
Вы создадите два файла для организации вашего кода:
profile.rs: Определите публичную структуруProfileс двумя полями:username(String) иnickname(Option<String>). Реализуйте:- Конструктор
new, который принимает имя пользователя и необязательный никнейм - Метод
display_name, который возвращает никнейм, если он существует, или имя пользователя в качестве запасного варианта — используйтеunwrap_orдля обработки этого случая - Метод
formatted_nickname, который возвращаетOption<String>с никнеймом, заключенным в квадратные скобки (например,"[CoolNick]"), если он существует, илиNone, если никнейма нет — используйтеmapдля преобразования значения
- Конструктор
main.rs: Подключите модуль profile и создайте два профиля — один с никнеймом и один без него. Продемонстрируйте работу обоих методов для каждого профиля, чтобы показать, как методыOptionпо-разному обрабатывают наличие и отсутствие значений.
Для метода formatted_nickname используйте отладочное форматирование ({:?}) при выводе на печать, так как он возвращает Option.
Ваш вывод должен соответствовать этому точному формату, показывая оба профиля:
Display name: {name}
Formatted nickname: {option}
Display name: {name}
Formatted nickname: {option}Например, для профиля с именем пользователя "alice" и никнеймом "Ace", и другого профиля с именем пользователя "bob" без никнейма, вывод будет следующим:
Display name: Ace
Formatted nickname: Some("[Ace]")
Display name: bob
Formatted nickname: NoneВы получите три входных значения: имя первого пользователя, никнейм первого пользователя и имя второго пользователя (у которого нет никнейма).
Шпаргалка
Тип Option<T> предоставляет методы для работы с опциональными значениями без явного сопоставления.
Метод unwrap_or возвращает внутреннее значение, если оно существует, или значение по умолчанию, если это None:
let maybe_name: Option<String> = Some(String::from("Alice"));
let name = maybe_name.unwrap_or(String::from("Guest"));
println!("{}", name); // Alice
let empty: Option<String> = None;
let fallback = empty.unwrap_or(String::from("Guest"));
println!("{}", fallback); // Guest
Метод map преобразует внутреннее значение, если оно существует, оставляя None без изменений:
let maybe_num: Option<i32> = Some(5);
let doubled = maybe_num.map(|x| x * 2);
println!("{:?}", doubled); // Some(10)
let nothing: Option<i32> = None;
let still_nothing = nothing.map(|x| x * 2);
println!("{:?}", still_nothing); // None
Эти методы позволяют выстраивать цепочки операций над значениями Option без постоянной проверки на None.
Попробуйте сами
mod profile;
use profile::Profile;
fn main() {
// Чтение входных данных
let mut input1 = String::new();
std::io::stdin().read_line(&mut input1).expect("Failed to read line");
let username1 = input1.trim().to_string();
let mut input2 = String::new();
std::io::stdin().read_line(&mut input2).expect("Failed to read line");
let nickname1 = input2.trim().to_string();
let mut input3 = String::new();
std::io::stdin().read_line(&mut input3).expect("Failed to read line");
let username2 = input3.trim().to_string();
// TODO: Создать первый профиль с username1 и nickname1 (Some)
// TODO: Создать второй профиль с username2 и без никнейма (None)
// TODO: Вывести display_name и formatted_nickname для первого профиля
// Использовать {:?} для formatted_nickname, так как он возвращает Option
// TODO: Вывести display_name и formatted_nickname для второго профиля
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Методы и поведение
Введение в блоки реализацииПараметр SelfИзменяемые методыАссоциированные функцииНесколько блоков реализацииЦепочки вызовов методовИтоги: действия с Rectangle4Проект: Виртуальный питомец
Определение питомцаКормление питомца2Инкапсуляция и модули
Основы модулейКлючевое слово PublicПриватные поляГеттерыСеттерыИтоги — Безопасный сейф5Обобщения
Обобщённые структурыОбобщённые методыНесколько обобщённых типовОбобщённые функцииПовторение — Координатная точка8Трейты как ограничения
Синтаксис ограничений трейтовМножественные ограниченияКонструкция whereВозврат типов с трейтамиИтоги — Обобщённый принтер11Паттерны проектирования в Rust
Паттерн NewtypeКомпозицияТрейт DropFrom и IntoИтоги — Мок-объект Smart Pointer3Продвинутые перечисления
Перечисления с даннымиМетоды в перечисленияхСопоставление вариантов с даннымиСнова об Option EnumИтоги — перечисление Shape6Определение трейтов
Что такое трейт?Реализация трейтовРеализации по умолчаниюПереопределение реализаций по умолчаниюТрейты с параметрамиИтоги — Media Player