#error-handling #rust #try-catch
#обработка ошибок #Ржавчина #try-catch
Вопрос:
Возможно ли обрабатывать несколько разных ошибок одновременно, а не по отдельности в Rust без использования дополнительных функций? Короче говоря: что такое эквивалент Rust для оператора try-catch?
Подобная функция (первоклассная обработка ошибок с ?
помощью and catch
) была предложена еще в 2016 году, но я не могу сказать, что из этого получилось и как может выглядеть решение такой проблемы в 2019 году.
Например, делать что-то вроде этого:
try {
do_step_1()?;
do_step_2()?;
do_step_3()?;
// etc
} catch {
alert_user("Failed to perform necessary steps");
}
Вместо:
match do_steps() {
Ok(_) => (),
_ => alert_user("Failed to perform necessary steps")
}
// Additional function:
fn do_steps() -> Result<(), Error>{
do_step_1()?;
do_step_2()?;
do_step_3()?;
// etc
Ok(())
}
В моей программе есть функция, которая проверяет множество разных мест в реестре на наличие разных значений данных и возвращает некоторые совокупные данные. Потребовалось бы использовать многие из этих операторов try-cache с try-catch внутри других try-catch внутри циклов.
Ответ №1:
В Rust нет инструкции try catch. Самый близкий подход — ?
оператор.
Однако вам не нужно создавать функцию и match
оператор, чтобы разрешить ее в конце. Вы можете определить замыкание в своей области и использовать ?
operator внутри замыкания. Затем броски удерживаются в возвращаемом значении закрытия, и вы можете перехватить это где угодно, например, следующим образом:
fn main() {
let do_steps = || -> Result<(), MyError> {
do_step_1()?;
do_step_2()?;
do_step_3()?;
Ok(())
};
if let Err(_err) = do_steps() {
println!("Failed to perform necessary steps");
}
}
Возможно ли обрабатывать несколько разных ошибок одновременно, а не по отдельности в Rust без использования дополнительных функций?
В настоящее время в Rust в основном рекомендуется использовать ящик для управления ошибками.
В качестве альтернативы в Rust есть ящик для управления ошибками. Используя Failure
, вы можете объединять, преобразовывать, объединять ошибки. После преобразования типов ошибок в один общий тип вы можете легко их перехватить (обработать).
Комментарии:
1. Просто для включения, но
failure
это не единственный ящик, который помогает в управлении ошибками. Их много, каждый с разным фокусом.2. Обратите внимание, что ваше выражение закрытия — это именно то, в качестве чего
try
предназначен блок .3. Наиболее рекомендуемым в настоящее время является
anyhow
.4. @rsalmei, спасибо, что указали на новейший обновленный ящик для этого, я также отредактировал свой ответ 😉
5. Для моих целей эта настройка оказалась более полезной: play.rust-lang.org /…
Ответ №2:
Result
s в Rust можно связать с помощью and_then
. Так что вы можете сделать это:
if let Err(e) = do_step_1().and_then(do_step_2).and_then(do_step_3) {
println!("Failed to perform necessary steps");
}
или, если вам нужен более компактный синтаксис, вы можете сделать это с помощью макроса:
macro_rules! attempt { // `try` is a reserved keyword
(@recurse ($a:expr) { } catch ($e:ident) $b:block) => {
if let Err ($e) = $a $b
};
(@recurse ($a:expr) { $e:expr; $($tail:tt)* } $($handler:tt)*) => {
attempt!{@recurse ($a.and_then (|_| $e)) { $($tail)* } $($handler)*}
};
({ $e:expr; $($tail:tt)* } $($handler:tt)*) => {
attempt!{@recurse ($e) { $($tail)* } $($handler)* }
};
}
attempt!{{
do_step1();
do_step2();
do_step3();
} catch (e) {
println!("Failed to perform necessary steps: {}", e);
}}
Ответ №3:
Существует также нестабильная функция, называемая try_blocks
(https://doc.rust-lang.org/beta/unstable-book/language-features/try-blocks.html , https://github.com/rust-lang/rust/issues/31436 )
Пример использования:
#![feature(try_blocks)]
fn main() {
// you need to define the result type explicitly
let result: Result<(), Error> = try {
do_step_1()?;
do_step_2()?;
do_step_3()?;
};
if let Err(e) = result {
println!("Failed to perform necessary steps, ({:?})", e);
}
}
fn do_step_1() -> Result<(), Error> { Ok(()) }
fn do_step_2() -> Result<(), Error> { Ok(()) }
fn do_step_3() -> Result<(), Error> { Err(Error::SomeError) }
#[derive(Debug)]
enum Error {
SomeError,
}
Ответ №4:
Я думаю match
, что выражение эквивалентно try/catch
match get_weather(location) {
Ok(report) => {
display_weather(location, amp;report);
}
Err(err) => {
println!("error querying the weather: {}", err);
// or write a better logic
}
}
Мы пытаемся получить отчет о погоде из api, если наш запрос не выполняется, обрабатываем ошибку, в противном случае отображается результат.
Ответ №5:
Не уверен, что это считается идиоматическим Rust, но вы можете использовать анонимное закрытие для достижения синтаксиса, подобного try/catch
:
fn do_step_1() -> Result<(), String> { Ok(()) }
fn do_step_2() -> Result<(), String> { Err("error at step 2".to_string()) }
fn do_step_3() -> Result<(), String> { Ok(()) }
fn alert_user(s: amp;str) { println!("{}", s); }
fn main() {
(|| {
do_step_1()?;
do_step_2()?;
do_step_3()?;
Ok(())
})().unwrap_or_else(|_err: String| {
alert_user("Failed to perform the necessary steps");
})
}
Ответ №6:
Концепция try
and except
используется в крайне расплывчатых терминах. Поскольку Rust является строго типизированным языком, пользователь должен написать свои собственные методы обработки ошибок, полагаясь на предоставленные Option<T>
Result<T, E>
перечисления и или определяя свои собственные привычные перечисления.
Смотрите Здесь для более подробного чтения для обработки ошибок с использованием перечислений.
try
Макрос устарел и был заменен ?
оператором, что упрощает организацию и очистку обработки ошибок, поскольку это может привести к беспорядку. Основное применение ?
оператора будет заключаться в том, что он позволяет реализовать функцию From для Result<T, E>
‘s Err(E)
variant .
Вот простой пример:
use std::num::ParseIntError;
// Custom error-based enum with a single example
#[derive(Debug)]
enum Error {
ParseIntError(ParseIntError),
// Other errors...
}
// Then implement the `From` trait for each error so that the `?` operator knows what to do for each specified error.
impl From<ParseIntError> for Error {
fn from(error: ParseIntError) -> Self {
Self::ParseIntError(error)
}
}
// When using the `?` try operator, if the `Result` is an `Err` then it will basically act as `return Err(E)` returning that error value out to the current scope. If it is `Ok(T)`, it will simply unwrap the variant.
fn main() -> Result<(), Error> {
// This will return the value `69` as a `u8` type
let parsed_value_1 = "69".parse::<u8>()?;
println!("{}", parsed_value_1);
// Since parsing fails here, a `ParseIntError` will be returned to the current function. *Since the scope is the `main` function, it will automatically print the error after panicking.
let parsed_value_2 = "poop".parse::<u8>()?;
// Unreachable code
println!("{}", parsed_value_2);
Ok(())
}
Комментарии:
1.
try
Ключевое слово зарезервировано для будущего использования, не является устаревшим и имеет мало связи с (устаревшим)try!
макрокомандой, которая была заменена?
оператором.