微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么不能用? 问号直接在 Rust 中的 collect() 上?

如何解决为什么不能用? 问号直接在 Rust 中的 collect() 上?

假设我们有一个缓冲读取器,并希望从中收集行并使用 ? 传播任何错误

let lines: Vec<_> = rdr.lines().collect()?;

这不会编译,因为 error[E0277]: the size for values of type `str` cannot be kNown at compilation time

搜索答案是每个https://github.com/rust-lang/rust/issues/49391

let lines: Vec<_> = rdr.lines().collect::<Result<Vec<_>,_>>()?;

我的问题是需要这种方式,以及这个 ::<Result<Vec<_>,_>> 究竟意味着什么以及正在做什么。

请记住,我刚开始学习 Rust。

解决方法

有几个部分组合在一起意味着编译器需要一点帮助:

collect 具有泛型类型

我们可以从 the documentation of Iterator::try 中看到它接受一个名为 B 的泛型类型,这就是它返回的类型。函数返回泛型类型有点不寻常(更常见的是,它们接受具有泛型类型的参数),但返回泛型类型可以产生一些非常有趣的通用API,并且{{ 1}} 绝对是其中之一! The documentation of the collect method 稍微谈到了这一点。

稍微简化你的代码,这意味着编译器不能只看代码:

collect

并知道 let lines = rdr.lines().collect(); 应该有什么类型。它可以推断出这一点(例如,如果您从函数返回 lines,它可以告诉 lines 的返回类型应该与函数的返回类型完全相同,或者使用它作为另一个参数的参数,它可以统一这些类型),但就其本身而言,这还不够上下文。

那么 - 编译器可以使用什么上下文来计算它应该收集到的类型......好吧,你有一个 collect 的事实给了它一个提示,但是:

? 不仅适用于 ?s

Result 可用于实现(当前不稳定,可能即将更改)Try 特性的任何类型。这意味着编译器不能使用问号来推断您收集的类型是 ? - 也许您收集的是 Result 或其他实现Option

?还调用 Try

更糟糕的是,into 实际上并不只是返回 ? 的错误分支,它会在需要时尝试执行转换(例如,如果您有一个函数Result 你可以写 Result<(),Box<String>> 并且 Rust 会把它变成这样的:

Err(String::from("foo"))?

因为 if let Err(err) = Err(String::from("foo")) { return err.into(); } trait 是为这些类型实现的,所以 Into 可以转换为 String 使用 Box<String>,所以 Into 操作符可以节省您需要自己进行转换。

然而,这增加了整个间接级别,编译器不能总是做出一些“明显”的推断,因为错误类型 ? 返回的错误类型可能与函数,但可以转换为它!

输入描述

您可以放置​​此信息的一个地方是您可以归因于变量的类型。所以你可以写这个代码:

collect()

(这些 let lines: Result<Vec<_>,_> = rdr.lines().collect(); let lines = lines?; 中的每一个都是您要求编译器根据它可用的信息确定应该存在的类型的地方。实际上,{{1} } 与 _ 相同 - 如果编译器可以解决它,它会,但如果不能,它会给出一个错误,提示您需要提供更多信息)。

在此代码片段中,我们说“let lines = rdr.lines().collect(); 的返回类型必须是 let lines: _ = rdr.lines().collect();,其中 collect() 类型是某物的 Result(编译器、你解决了),而 Ok 类型是,好吧,编译器也请解决这个问题!

这为编译器提供了相当多的信息,在很多情况下就足够了,尽管有时我们需要自己填写更多的 Vec

类型归属需要一个变量 - 输入turbofish

但是我上面写的代码比你写的代码要冗长一些。我们有两个陈述而不是一个,我们制作了两个变量而不是一个。 Error 运算符的很多要点是让我们能够编写简洁的代码!

您发现的语法,俗称 turbofish(因为 _ 看起来有点有趣),是一种明确说明 ? 函数采用的泛型类型的方法:>

::<_>

这是说:调用函数collect,它的泛型类型应该是collect::<Result<Vec<_>,_>>() 。就像我们给变量的类型赋值一样,这是一种向编译器提供关于函数的泛型类型应该是什么的信息的方式。

所以把所有这些放在一起,我们可以写:

collect

这就是对编译器说的:我希望你调用 Result<Vec<_>,_> - 我希望 collect 返回的泛型类型是某物的 let lines = rdr.lines().collect::<Result<Vec<_>,_>>()?; collect(编译器可以解决其余的问题),然后它就会知道您要收集什么。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。