Тип осведомленной карты при использовании итератора - PullRequest
0 голосов
/ 06 декабря 2018

У меня есть следующий код:

fn main() {
    let xs = vec!["1i32".to_string(), "2".to_string(), "3".to_string()];
    let ys = do_some(xs);
    println!("{:?}", ys);
}

fn do_some(tokens: Vec<String>) -> Result<Vec<String>, bool> {
    tokens
        .into_iter()
        .map(|token: String| Ok(token))
        .map(|token: Result<String, bool>| token.map(|s| s + "a"))
        .fold(Ok(Vec::new()), |acc, s| acc.push(s))
}

Rust Playground

После строки .map(|token| Ok(token)) я ожидаю, что вызов map будет действовать наResult, а не Iterator и, таким образом, можно развернуть Result, но я получаю Result<String, bool> в качестве выходных данных:

error[E0599]: no method named `push` found for type `std::result::Result<std::vec::Vec<std::string::String>, bool>` in the current scope
  --> src/main.rs:12:44
   |
12 |         .fold(Ok(Vec::new()), |acc, s| acc.push(s))
   |                                            ^^^^

Каков наилучший способ написания функционально-подобного кодав Rust?

Мне известно о and_then, но, похоже, его невозможно использовать в этой цепочке.

Ответы [ 2 ]

0 голосов
/ 07 декабря 2018

Я не могу точно сказать, что именно вы ищете, но - судя по сигнатуре do_some - я полагаю, что у вас есть Vec<String>, функция, работающая на String, возвращающая Result<String, bool>, иВы хотите применить эту функцию к каждому элементу в Vec и получить их в Vec, если все Ok.Если вы встретите Err, вы хотите вернуть Err.Это может быть достигнуто следующим образом:

fn do_some(tokens: Vec<String>) -> Result<Vec<String>, bool> {
    tokens
        .into_iter()
        .map(|token: String| Ok(token)) // or any other Fn(String)->Result<String, bool>
        .map(|token: Result<String, bool>| token.map(|s| s + "a"))
        .collect()
}

Это использует collect, полагаясь на FromIter, и вы можете преобразовать итератор по Result s вResult.

Обратите внимание, что вы можете сделать все это более общим:

  • Вход не обязательно должен быть Vec, это может бытьIterator более String с.
  • Мы можем параметризовать его с помощью функции преобразования, которая преобразует один String в Result<String, bool>, или - чтобы быть более общим - в Result<String, ErrorType> для некоторыхErrorType.
  • Вам не обязательно нужен Vec в качестве успешного типа возврата, но все, что реализует FromIter (который часто является контейнерным типом).

Первый шаг для этого может выглядеть следующим образом:

fn do_some<
    ErrorType,
    Tokens: std::iter::IntoIterator<Item=String>, // an iterable yielding String
    StringToResult: Fn(String) -> Result<String, ErrorType>,
>(
    tokens: Tokens,
    string_to_result: StringToResult,
) -> Result<Vec<String>, ErrorType> {
    tokens
        .into_iter()
        .map(|s| string_to_result(s).map(|s| s + "a"))
        .collect()
}

, который можно использовать следующим образом:

fn main() {
    println!("{:?}",
        do_some(vec!["i1".to_string(), "i2".to_string(), "i3".to_string()], |s| {
            if s.starts_with("i") {
                Ok(s)
            } else {
                Err(s + " i does not start with i")
            }
        })
    );
    println!("{:?}",
        do_some(vec!["i1".to_string(), "i2".to_string(), "A3".to_string()], |s| {
            if s.starts_with("i") {
                Ok(s)
            } else {
                Err(s + " i does not start with i")
            }
        })
    );
}
0 голосов
/ 06 декабря 2018

Вы можете собрать результаты в промежуточный контейнер, а затем обработать оттуда любые ошибки (обратите внимание, что вам нужно будет изменить возвращаемое значение вашей функции):

fn internal_eval(&mut self, tokens: Vec<String>) -> Result<(), Error> {
    tokens
        // Use `into_iter` to iterate over owned objects
        .into_iter()
        .map(|token| parse_token(token))
        .collect::<Result<Vec<_>, _>>()?
        .into_iter()
        .map(|token1| do_something_with_token1(token1))
        .fold(some_init, |acc, x| {
            // reduce
        });
    // ...
}

Если вы не можетепозволить себе создать промежуточный контейнер, вы также можете сделать это (чуть менее читабельно):

fn internal_eval(&mut self, tokens: Vec<String>) -> Result<(), Error> {
    tokens
        .into_iter()
        .map(|token| parse_token(token))
        .map(|token1_res| token1_res.map(|token1| do_something_with_token1(token1) ))
        .try_fold(some_init, |acc, x| -> Result<SomeType, Error> {
            let val = x??;
            // ...
        });
    Ok(())
}

Обновление

fn do_some(tokens: Vec<String>) -> Result<Vec<String>, bool> {
    Ok(tokens
        .into_iter()
        .map(|token: String| Ok(token))
        .map(|token: Result<String, bool>| token.map(|s| s + "a"))
        .try_fold(Vec::new(), |mut acc, s| { acc.push(s?); Ok(acc) })?)
}
...