首页 > 解决方案 > 为什么 Rust 编译器可以在单独的行上推断类型,但如果组合成一行则不能?

问题描述

为什么允许这样做:

let payload = "key1=value1";
let value: Vec<&str> = payload.split("=").collect();
let value = value[1];
println!("value is {:?}", value);

但不是这个:

let payload = "key1=value1";
let value: Vec<&str> = payload.split("=").collect()[1];
println!("value is {:?}", value);

这会导致错误:

error[E0282]: type annotations needed
 --> src/main.rs:3:47
  |
3 |     let value: Vec<&str> = payload.split("=").collect()[1];
  |                                               ^^^^^^^
  |                                               |
  |                                               cannot infer type for type parameter `B` declared on the method `collect`
  |                                               help: consider specifying the type argument in the method call: `collect::<B>`
  |
  = note: type must be known at this point

两者不是等价的吗?它们是相同的要被解析的标记,编译器,只是分成不同的行。

我不明白第一个语法中存在哪些额外信息以使推理成为可能。

正如编译器警告的那样,也许这与不可能进行推理无关,但由于 Rust 语法、约定等原因,这是不允许的?

我试图理解为什么编译器无法推断它(即逻辑上缺少信息?只是由于约定?)而不是如何解决这个特定情况。

在编译器/解析器级别考虑这一点,

表达式let value: Vec<&str> = payload.split("=").collect() 后缀[1]

A(可接受):

(expression)
(expression')(suffix)

B(不可接受):

(expression)(suffix)

考虑到expression'基本上添加value = value,这是一个重言式并且不添加任何新信息,

如果expressionexpression'具有相同程度的信息,为什么 B 对编译器无效?如果没有新信息,为什么它不能在 B 中推断出相同的东西?

我特别质疑“无法推断”部分,而不是“由于 Rust YY 语法/约定应该是 XX”)。

这只是编译器限制或强制易读性的故意错误,还是从一个案例添加到另一个案例的独特信息?(逻辑上不同的场景)。

标签: typessyntaxrustcompiler-constructiontype-inference

解决方案


第一个片段,

let value: Vec<&str> = payload.split("=").collect();
let value = value[1];

是说将一个迭代器收集到一个向量(&strs)中,然后将索引处的元素分配1value(这将是 a &str)。请注意,第一个value被第二个所掩盖(它们具有不同的类型,因此如果您希望它们具有相同的名称,则需要执行此操作)。

部分由于类型注释,单行版本说的是不同的东西。

let value: Vec<&str> = payload.split("=").collect()[1];

这就是说将迭代器收集到 ...something... 并将其索引到1并且结果类型应该是Vec<&str>. 现在,由于在索引时可能会生成任意数量的类型Vec<&str>(它可能是Vec<Vec<&str>>HashMap<usize, Vec<&str>>甚至可能是此项目中定义的某些类型或依赖项),Rust 不会尝试在这里猜测。由于它不知道要收集到哪种类型,所以它放弃并返回该错误。

要正确执行此操作(在一行中),您可能希望使用 ol' turbofish 语法来指示您想要收集的类型。

let value = payload.split("=").collect::<Vec<_>>()[1];

(操场)

请注意,元素的类型Vec是已知的,因为它只是迭代器元素的类型。这就是为什么我们可以Vec<_>用来让编译器推断出正确的类型。

还有一件事。为了更惯用,您可以charpayload.split("="). payload.split'=')也可以。


推荐阅读