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

问题描述 投票:-1回答:3

为什么允许这样做:

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语法,约定等原因,这是不允许的。

我正在尝试理解为什么编译器无法推断(即逻辑上丢失信息?只是由于约定?)而不是如何来解决此特定情况。

关于编译器/解析器级别的思考,

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

A(可接受):

(expression)
(expression')(suffix)

B(不可接受):

(expression)(suffix)

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

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

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

这是否只是编译器的限制或旨在增强可读性的故意错误,还是在一个案例中添加了唯一信息? (逻辑上不同的场景)。

types syntax rust compiler-construction type-inference
3个回答
3
投票

第一段,

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

就是说将一个迭代器收集到一个(&str s)向量中,然后将索引1处的元素分配给value(可能是&str)。请注意,第一个value被第二个阴影遮蔽(它们具有不同的类型,因此,如果希望它们具有相同的名称,则需要执行此操作)。

部分由于类型注释,单行版本说的有所不同。

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

这是说将迭代器收集到...某物中,并在1处对其进行索引,结果类型应为Vec<&str>。现在,由于建立索引时可能会生成Vec<&str>的任何类型(可能是Vec<Vec<&str>>HashMap<usize, Vec<&str>>或什至是此项目或依赖项中定义的某种类型),因此Rust不会在这里进行猜测。由于不知道要收集哪种类型,因此它放弃并返回该错误。

为了正确地做到这一点(一行),您可能希望使用ol'turbofish语法来表示要收集到的类型类型。

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

(playground)

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

还有一件事。为了更加惯用,可以使用char代替payload.split("=")中的单个字符串。 payload.split'=')也可以。


0
投票

您的类型注释在第二个示例中变得无效。您想要的是这个:

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

rust playground link

可以简化为:

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

之所以需要明确说明,是因为Iterator<Item = &str>可以被收集为十二种不同的收集类型,并且编译器无法猜测您想要哪种类型。


0
投票

您正在迭代器上使用collect方法,该方法需要一个“类型”来知道它应该收集到的内容。

此外,您也不必为了提取第一个令牌而进行收集

collect

如果您想使用第二个示例(效率低下),则可以使用turbofish语法实现它

let value = payload.split("=").nth(1).unwrap(); // you need to better error handling
println!("value is {:?}", value);

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

© www.soinside.com 2019 - 2024. All rights reserved.