Как найти индекс элемента в таблице результатов, останавливаясь, если обнаружена ошибка?

Предположим, у меня есть коллекция элементов Результат, например:

let items: &[Result<&str, u32>] = &[Ok("foo"), Err(444), Ok("bar")];

Мне нужно найти индекс первого элемента со значением "bar". Моя первая попытка выглядит так:

let bar_idx = items.iter()
    .position(|item| item? == "bar")?;

Это не сработает из-за оператора ? внутри замыкания.

Мне известен метод try_for_each , который завершает работу всей коллекции неудачно, если возвращается Err, но такого эквивалента для позиции не существует, что я и пытаюсь сделать.

Как реализовать этот поиск?

Обновлено:

Я открыл вопрос в репозитории Rust, потому что считаю, что try_position должен существовать.

Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
0
90
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы можете построить его поверх try_for_each():

use std::ops::ControlFlow;
let item: ControlFlow<Result<usize, u32>> =
    items
        .iter()
        .enumerate()
        .try_for_each(|(idx, item)| match item {
            Ok(v) if *v == "bar" => ControlFlow::Break(Ok(idx)),
            Ok(_) => ControlFlow::Continue(()),
            Err(err) => ControlFlow::Break(Err(*err)),
        });
// Can replace by `ControlFlow::break_value()` once stabilized.
let item = match item {
    ControlFlow::Break(v) => Some(v),
    ControlFlow::Continue(()) => None,
};

Но, честно говоря, лучше использовать простой цикл for:

let mut result = None;
for (idx, item) in items.iter().enumerate() {
    match item {
        Ok(v) if *v == "bar" => {
            result = Some(Ok(idx));
            break;
        }
        Ok(_) => {}
        Err(err) => {
            result = Some(Err(*err));
            break;
        }
    }
}

Версия с циклом for станет еще проще, если вы обернете ее функцией, возвращающей Result, и используете ? для раннего выхода при ошибке: play.rust-lang.org/…

Sven Marnach 26.03.2024 13:58
fn main() {
    let items: &[Result<&str, u32>] = &[Ok("foo"), Err(404), Ok("bar")];

    // find the index of the first element with the value "bar"
    let idx1 = items.iter().position(|x| x.is_ok_and(|x| x == "bar"));
    println!("{:?}", idx1);

    // or you can combine `enumerate`,`find` and `map` to find the index
    let idx2 = items.iter()
        .enumerate()
        .find(|(index, value)| {
            if **value == Ok("bar") {
                true
            } else {
                false
            }
        })
        .map(|x| x.0);
    println!("{:?}", idx2);
}

Оба выведите Some(2) сюда. Если ничего не найдено, выведите None.

if <expr> { true } else { false } то же самое, что и просто <expr>.
Filipe Rodrigues 26.03.2024 05:02

Ни одно из этих предложений не приводит к ошибке при обнаружении ошибки на раннем этапе, поэтому я думаю, что это не соответствует сути вопроса.

Sven Marnach 26.03.2024 13:57

Другие вопросы по теме