Перейти к содержимому

Option в Rust

В Rust нет null. Если значения может не быть, это выражается прямо в типе с помощью Option. Поначалу кажется, что это лишние церемонии, но так закрывается целый класс ошибок — «нулевые ссылки», из-за которых в других языках случилось бесчисленное множество падений.

Option — это перечисление с двумя вариантами:

enum Option<T> {
Some(T), // значение есть
None, // значения нет
}

На самом деле ты его уже видел: .get() у вектора и .get() у HashMap возвращают Option.

fn main() {
let has_value: Option<i32> = Some(5);
let no_value: Option<i32> = None;
println!("{:?}", has_value);
println!("{:?}", no_value);
}

Вывод:

Some(5)
None
▶ Попробуй сам

Безопасный способ достать значение — match. Rust заставляет обработать оба случая, так что случайно забыть про вариант «значения нет» не получится:

fn main() {
let age: Option<i32> = Some(30);
match age {
Some(value) => println!("Возраст: {value}"),
None => println!("Возраст не указан"),
}
}
▶ Попробуй сам
fn find_user(id: u32) -> Option<&'static str> {
if id == 1 {
Some("Алиса")
} else {
None
}
}
fn main() {
match find_user(1) {
Some(name) => println!("Найден: {name}"),
None => println!("Пользователь не найден"),
}
match find_user(99) {
Some(name) => println!("Найден: {name}"),
None => println!("Пользователь не найден"),
}
}

Вывод:

Найден: Алиса
Пользователь не найден
▶ Попробуй сам

Вместо того чтобы вернуть null или выбросить исключение, когда пользователь не найден, функция самим своим типом сообщает каждому, кто её вызывает: «я могу ничего не найти». И компилятор не даст проигнорировать такую возможность.

В простых случаях полный match может показаться перебором. Есть пара сокращений:

fn main() {
let age: Option<i32> = None;
let value = age.unwrap_or(0); // если значения нет, берём 0
println!("{value}");
}
▶ Попробуй сам

Есть ещё .unwrap(), который сразу отдаёт значение, но паникует (аварийно завершает программу), если там None:

let age: Option<i32> = Some(30);
let value = age.unwrap(); // здесь всё в порядке, ведь age — это Some

Напиши fn first_even(numbers: &[i32]) -> Option<i32>, которая возвращает первое чётное число в срезе или None, если такого нет. Выведи результат для [3, 7, 8, 10] через match, а для [1, 3, 5] — через .unwrap_or(0). Подсказка: перебирай срез в цикле и делай return Some(n), как только n % 2 == 0; после цикла верни None.

Показать решение
fn first_even(numbers: &[i32]) -> Option<i32> {
for &n in numbers {
if n % 2 == 0 {
return Some(n);
}
}
None
}
fn main() {
match first_even(&[3, 7, 8, 10]) {
Some(n) => println!("Первое чётное: {n}"),
None => println!("Чётных чисел нет"),
}
let fallback = first_even(&[1, 3, 5]).unwrap_or(0);
println!("Со значением по умолчанию: {fallback}");
}

Вывод:

Первое чётное: 8
Со значением по умолчанию: 0
▶ Попробуй сам

Создай HashMap с настройками, где есть только "громкость" со значением 80. Затем перебери ключи "громкость" и "яркость" и выведи значение, если оно есть, или сообщение «не задана», если его нет. Подсказка: .get(key) у HashMap возвращает Option, так что по нему можно сразу сделать match.

Показать решение
use std::collections::HashMap;
fn main() {
let mut settings = HashMap::new();
settings.insert("громкость", 80);
for key in ["громкость", "яркость"] {
match settings.get(key) {
Some(value) => println!("{key} = {value}"),
None => println!("{key} не задана"),
}
}
}

Вывод:

громкость = 80
яркость не задана
▶ Попробуй сам
  • Option<T> описывает значение, которое может быть, а может и не быть, — null в Rust нет.
  • Some(value) что-то содержит; None значит, что там ничего нет.
  • match — безопасный способ обработать оба случая; компилятор не даст забыть ни один.
  • .unwrap_or(default) даёт запасное значение; .unwrap() паникует, если значение None, — пользуйся им пореже.

Проверь себя

1. Что Rust использует вместо null для значения, которого может не быть?

2. Что происходит, если вызвать .unwrap() у значения None?

3. Что делает age.unwrap_or(0), когда age — это None?

4. Почему match считается безопасным способом достать значение из Option?

5. Какие из упомянутых на этой странице методов уже возвращают Option?

Результат: 0 / 5