На 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 и оператора ? обработка ошибок оптимальнее, кодовая база устойчивее. Благодаря этим методам, ошибки и непредвиденные условия обрабатываются программой корректно, без аварийного завершения.
Читайте также:
- Многопоточность на Rust: ускоряем приложения, делаем их эффективнее
- LOESS в Rust
- Изучаем Rust. Потоковая передача tar-архива
Читайте нас в Telegram, VK и Дзен
Перевод статьи Andriat Ratyanto: Mastering Rust Error Handling: Ditch unwrap and Write Safer, Cleaner Code