避免在collect() 上使用turbofish 类型注释到Result<Vec<_>, _>

epi*_*ian 3 rust

我想使用s 到 acollect()的迭代器并提前返回,以防迭代器中的任何元素出错。所以,像这样:Result<T, E>Vec<T>

use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    let input = ""; // Snip real input
    let games: Vec<Game> = input.lines().map(parse_game).collect()?;
    println!("parsed {} games", games.len());
    Ok(())
}

struct Game;

fn parse_game(_s: &str) -> Result<Game, Box<dyn Error>> {
    Ok(Game)
}
Run Code Online (Sandbox Code Playgroud)

Playground 链接,是的,该代码的灵感来自于 Advent of Code 2023 day 2)

但这不起作用,它无法编译并出现错误:

error[E0282]: type annotations needed
 --> src/main.rs:5:58
  |
5 |     let games: Vec<Game> = input.lines().map(parse_game).collect()?;
  |                                                          ^^^^^^^ cannot infer type of the type parameter `B` declared on the method `collect`
  |
help: consider specifying the generic argument
  |
5 |     let games: Vec<Game> = input.lines().map(parse_game).collect::<Vec<_>>()?;
  |                                                                 ++++++++++
Run Code Online (Sandbox Code Playgroud)

如果我添加类型注释以使用正确的impl FromIteratoron collect(),它会起作用:

error[E0282]: type annotations needed
 --> src/main.rs:5:58
  |
5 |     let games: Vec<Game> = input.lines().map(parse_game).collect()?;
  |                                                          ^^^^^^^ cannot infer type of the type parameter `B` declared on the method `collect`
  |
help: consider specifying the generic argument
  |
5 |     let games: Vec<Game> = input.lines().map(parse_game).collect::<Vec<_>>()?;
  |                                                                 ++++++++++
Run Code Online (Sandbox Code Playgroud)

游乐场链接

但我不明白为什么这种涡轮鱼类型注释::<Result<_, _>>是必要的。我发现它非常多余而且吵闹。

我希望 Rust 编译器知道parse_game返回Results,并且如果我想收集到 a Vec<Game>(由变量类型指定games)并且我提前返回返回( )?的函数上的运算符,那么调用也应该返回,因此不需要显式类型注释。Resultmain()collect()Result

那么,有没有办法摆脱这个turbofish类型注释呢?或者其他一些惯用的表达方式?

dre*_*ato 5

有很多方法可以摆脱涡轮鱼,但你不会喜欢它们。第一种方法是使用中间变量。

let games_result: Result<_, _> = input.lines().map(parse_game).collect();
let games: Vec<Game> = games_result?;
Run Code Online (Sandbox Code Playgroud)

第二种方法是使用 的collect内部特征函数FromIterator::from_iter

let games: Vec<Game> = Result::from_iter(input.lines().map(parse_game))?;
Run Code Online (Sandbox Code Playgroud)

第一个在噪音方面没有比涡轮鱼有任何改进,第二个则失去了 的功能风格collect

不幸的是,我认为没有更好的方法来避免涡轮鱼collect。然而, itertools crate 提供了try_collect,它可以满足您的需求。

use itertools::Itertools;
let games: Vec<Game> = input.lines().map(parse_game).try_collect()?;
Run Code Online (Sandbox Code Playgroud)