Как сделать обработку ошибок вложенных `.iter ()`? - PullRequest
1 голос
/ 12 октября 2019

Я пытаюсь выполнить обработку ошибок при использовании .iter() в сочетании с .flat_map, в котором есть .iter().map().

Сценарий получает все Event, которые принадлежат группе организаций, где вложенные .iter().map() используются для получения участников каждого события, объединяют это с событием и возвращают EventResponse stuct.

Неописанным способом описания проблемы может быть «Как получить Result<Vec<T>, err> из вложенного flat_map Result<Vec<T>, err>, который имеет вложенную карту Result<T, err>»

Ниже приведена абстрагированная / упрощенная версия кода, который я использую, который дает мне те же ошибки, что и мой настоящий код.

struct Event {
    id: usize,
}

#[derive(Debug)]
struct EventResponse {
    id: usize,
    participants: Vec<i32>,
}

fn main() {
    let orgs = vec![1, 3, 14, 12];

    let events: Result<Vec<EventResponse>, &str> = orgs
        .iter()
        .flat_map::<Result<Vec<EventResponse>, &str>, _>(|org_id| {
            get_events_for(*org_id)
                .map_err(|_| "That Org id does not exist")
                .map(|events| {
                    events
                        .iter()
                        .map::<Result<EventResponse, &str>, _>(|event| {
                            get_event_with_participants(event)
                                .map(|event_response| event_response)
                                .map_err(|_| "Participants for that event were not found")
                        })
                        .collect()
                })
        })
        .collect();
}

fn get_events_for(id: usize) -> Result<Vec<Event>, ()> {
    // pretend we are checking against a database of existing org ids, if the org id does not exist, then return an error
    if id == 3 {
        Ok(vec![Event { id }])
    } else {
        Err(())
    }
}

fn get_event_with_participants(event: &Event) -> Result<EventResponse, ()> {
    //pretend the participants are fetched from a database
    let foundParticipants = true;
    if foundParticipants {
        Ok(EventResponse {
            id: event.id,
            participants: vec![1, 2, 5],
        })
    } else {
        Err(())
    }
}

Playground

Типовые аннотации должны показывать, что ожидается на каждом этапе. Я ожидаю, что events будет иметь тип Result<Vec<EventResponse>, &str>, но я получаю 2 ошибки:

error[E0277]: a collection of type `std::vec::Vec<EventResponse>` cannot be built from an iterator over elements of type `std::result::Result<EventResponse, &str>`
  --> example.rs:27:26
   |
27 |                         .collect()
   |                          ^^^^^^^ a collection of type `std::vec::Vec<EventResponse>` cannot be built from `std::iter::Iterator<Item=std::result::Result<EventResponse, &str>>`
   |
   = help: the trait `std::iter::FromIterator<std::result::Result<EventResponse, &str>>` is not implemented for `std::vec::Vec<EventResponse>`

error[E0277]: a collection of type `std::result::Result<std::vec::Vec<EventResponse>, &str>` cannot be built from an iterator over elements of type `std::vec::Vec<EventResponse>`
  --> example.rs:30:10
   |
30 |         .collect();
   |          ^^^^^^^ a collection of type `std::result::Result<std::vec::Vec<EventResponse>, &str>` cannot be built from `std::iter::Iterator<Item=std::vec::Vec<EventResponse>>`
   |
   = help: the trait `std::iter::FromIterator<std::vec::Vec<EventResponse>>` is not implemented for `std::result::Result<std::vec::Vec<EventResponse>, &str>`

РЕДАКТИРОВАТЬ: get_events_for функция не может быть изменена , однако, get_event_with_participants функция может быть изменена , если это поможет.

1 Ответ

1 голос
/ 12 октября 2019

Заинтересовавшись проблемой, я попытался реализовать это с помощью map и try_fold. Использование flat_map напрямую не работало для меня, так как я не мог обойти тот факт, что первый внутренний цикл должен выдавать Result. Я переместил сообщения об ошибках в функции, но их легко можно было бы отменить, если бы вы явно хотели их избежать.

struct Event {
    id: usize,
}

#[derive(Debug)]
struct EventResponse {
    id: usize,
    participants: Vec<i32>,
}

fn main() {
    let orgs = vec![1, 3, 14, 12];

    let events: Result<Vec<EventResponse>, &str> = orgs
        .iter()
        .map(|org_id| {
            get_events_for(*org_id)?
                .iter()
                .map(get_event_with_participants)
                .collect::<Result<Vec<_>, _>>()
        })
        .collect::<Result<Vec<_>, _>>()
        .map(|org_responses| org_responses.into_iter().flatten().collect());
}

fn get_events_for(id: usize) -> Result<Vec<Event>, &'static str> {
    // pretend we are checking against a database of existing org ids, if the org id does not exist, then return an error
    if id == 3 {
        Ok(vec![Event { id }])
    } else {
        Err("That Org id does not exist")
    }
}

fn get_event_with_participants(event: &Event) -> Result<EventResponse, &'static str> {
    //pretend the participants are fetched from a database
    let foundParticipants = true;
    if foundParticipants {
        Ok(EventResponse {
            id: event.id,
            participants: vec![1, 2, 5],
        })
    } else {
        Err("Participants for that event were not found")
    }
}

Для try_fold основной функцией станет:

fn main() {
    let orgs = vec![1, 3, 14, 12];

    let events: Result<Vec<EventResponse>, &str> =
        orgs.iter().try_fold(Vec::new(), |mut responses, &org| {
            responses = get_events_for(org)?
                .into_iter()
                .try_fold(responses, |mut responses, event| {
                    let response = get_event_with_participants(&event)?;
                    responses.push(response);
                    Ok(responses)
                })?;
            Ok(responses)
        });
}

Лично я фанат версии map, так как изменение Vec с использованием try_fold выглядит неловко, поскольку вам нужно вернуть аккумулятор, несмотря на прямое изменение Vec.

Другая версиярассмотреть это просто использовать циклы, что в данной ситуации кажется гораздо проще, поскольку вы можете использовать оператор ? для ошибок:

fn main() {
    let orgs = vec![1, 3, 14, 12];

    let events = get_all_responses(orgs);
}

fn get_all_responses(
    orgs: impl IntoIterator<Item = usize>,
) -> Result<Vec<EventResponse>, &'static str> {
    let mut responses = Vec::new();
    for org in orgs.into_iter() {
        for event in get_events_for(org)? {
            responses.push(get_event_with_participants(&event)?)
        }
    }
    Ok(responses)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...