На Rust значения, оборачиваемые в Option или Result, обычно обрабатываются при помощи unwrap. Но, если значение None или Err, это чревато паниками. Такое поведение часто считается небезопасной или плохой практикой в коде продакшена: оно чревато аварийным завершением программы. Рассмотрим альтернативы unwrap  —  более безопасные и надежные.

unwrap

Методом unwrap на Rust извлекается значение внутри типа Option или Result. Если значение Some или Ok, возвращается обернутое значение. Если значение None или Err, метод «запаникует» и программа остановится.

let val = Some(1);
let x = val.unwrap(); // возвращается «1»
let y: Option<i32> = None;
let z = y.unwrap(); // «запаникует»

Это прекрасно для быстрого прототипирования, но рискованно в коде на продакшене, поскольку потенциальные ошибки не обрабатываются корректно.

Альтернативы unwrap

Выражения match

match  —  это явная обработка ошибок сопоставлением с возможными результатами: Some, None, Ok или Err.

let val: Option<i32> = Some(10);
match val {
Some(v) => println!("Value: {}", v),
None => println!("No Value"),

Выражения if let

Для одного случая  —  Some или Ok  —  применяется if let с простой обработкой ошибок.

let val: Option<i32> = Some(10);
if let Some(v) = val {
println!("Value: {}", v);
} else {
println!("No value");
}

Это короче, чем match, но безопасность сохраняется обработкой случая, при котором значение отсутствует.

Выражения while let

while let применяется, когда нужно постоянно сопоставлять с образцом значение внутри цикла, пока не перестает соблюдаться конкретное условие.

fn main() {
let mut numbers = vec![Some(1), Some(2), None, Some(3), None, Some(4)];

while let Some(Some(number)) = numbers.pop() {
println!("Processing number: {}", number);
}
}

unwrap_or и unwrap_or_else

Вместо того чтобы распаковывать с unwrap и паниковать, используется unwrap_or со значением по умолчанию, если результат None или Err.

let val: Option<i32> = None;
let x = val.unwrap_or(0); // Возвращается «0», если результат «None»

Для динамической обработки ошибок при помощи unwrap_or_else предоставляется функция, из которой в случае None или Err возвращается значение.

let val: Option<i32> = None;
let x = val.unwrap_or_else(|| {
println!("Defaulting to 0");
0
});

Оператор ?

При работе с типами Result распространение ошибок осуществляется оператором ? сокращенно: им автоматически возвращается ошибка, если значение Err, и без паники.

fn parse_number(s: &str) -> Result<i32, std::num::ParseIntError> {
let num = s.parse::<i32>()?;
Ok(num)
}

fn main() {
match parse_number("42") {
Ok(n) => println!("Parsed: {}", n),
Err(e) => println!("Error parsing: {}", e),
}
}

Таким подходом сокращается шаблонный код и избегается логика явной обработки ошибок, при этом сохраняется безопасное распространение ошибок.

Заключение

unwrap полезен в быстром или одноразовом коде, но чреват паниками. Поэтому его следует избегать в продакшене. С более безопасными альтернативами вроде match, unwrap_or и оператора ? обработка ошибок оптимальнее, кодовая база устойчивее. Благодаря этим методам, ошибки и непредвиденные условия обрабатываются программой корректно, без аварийного завершения.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Andriat Ratyanto: Mastering Rust Error Handling: Ditch unwrap and Write Safer, Cleaner Code

Предыдущая статьяСпособен ли Node.js справиться с миллионами пользователей?
Следующая статьяПочему имена Android-пакетов имеют вид com.xyz.abc?