Menu
Coddy logo textTech

Снова об 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 инкапсулирует концепцию «возможно, значение» и предоставляет чистый интерфейс для работы с ним. Вы вызываете методы объекта, а не пишете внешнюю логику для его проверки.

challenge icon

Задание

Легко

Давайте создадим систему профилей пользователей, которая изящно обрабатывает необязательные данные с помощью методов 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 для второго профиля
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Object Oriented Programming