此代码编译:
#[derive(Debug, Default)]
struct Example;
impl Example {
fn some_method(&self) {}
}
fn reproduction() -> Example {
let example = Default::default();
// example.some_method();
example
}
如果添加了注释行,则会导致错误:
error[E0282]: type annotations needed
--> src/lib.rs:10:5
|
9 | let example = Default::default();
| ------- consider giving `example` a type
10 | example.some_method();
| ^^^^^^^ cannot infer type
|
= note: type must be known at this point
为什么添加此方法调用导致类型推断失败?
我见过这两个问题:
从他们那里,我知道Rust使用了(modified)版本的Hindley-Milner。后一个问题有an answer,它将Rust的类型推断描述为一个方程组。 Another answer明确指出“Rust中的类型信息可以向后流动”。
利用这种应用于这种情况的知识,我们有:
example
是?E
型?E
必须有一个名为some_method
的方法?E
被退回Example
向后工作,人类很容易看到?E
必须是Example
。我能看到的内容与编译器可以看到的内容之间的差距在哪里?
基于已知事实(见下文),它无法编译,因为:
let example = Default::default();
,example
可以是任何实现Default
的东西,Default
的东西”都不是已知的类型。我用字段访问替换了some_method()
,它产生了同样的错误。
来自Type inference depends on ordering (#42333):
use std::path::PathBuf; pub struct Thing { pub f1: PathBuf, } fn junk() -> Vec<Thing> { let mut things = Vec::new(); for x in vec![1, 2, 3] { if x == 2 { for thing in things.drain(..) { thing.f1.clone(); } return vec![] } things.push(Thing{f1: PathBuf::from(format!("/{}", x))}); } things } fn main() { junk(); }
这会生成Rust 1.33.0的编译器错误:
error[E0282]: type annotations needed
--> src/main.rs:13:17
|
9 | let mut things = Vec::new();
| ---------- consider giving `things` a type
...
13 | thing.f1.clone();
| ^^^^^ cannot infer type
|
= note: type must be known at this point
您应该关注eddyb(Rust语言设计团队since May, 2016的知名成员)的以下评论。
这是有序类型检查器的已知限制。当推论自由流动时,
thing.f1.clone()
在things.push(Thing {...})
之前被检查,因此当你试图访问thing: Thing
字段时,不知道f1
。我们将来可能会远离这一点,但目前还没有计划。
更重要的是comment #2:
我的意思是类型检查器按照它编写的顺序遍历函数。 [...]除非已知类型,否则根本不支持字段访问和方法调用。
我不知道完整的答案,我几乎不知道Rust编译器的内部工作原理,但是我从Rust的经验中得到了一些推论。
有关Rust中类型的信息可以“向后流”,但有时Rust需要知道(绝对确定)表达式的类型。在这些情况下,它必须“已经”知道类型,即它不会继续向前看。
从我所看到的情况来看,这种情况仅限于方法调用。我怀疑它与可以在特征上实现方法的事实有关,这使事情变得非常复杂。我怀疑使用名为some_method
的方法在范围内有任何特征,但我认为每当Rust编译器遇到方法调用时,它都要求类型已经确定。
您可以通过对实现特征的类型的方法调用来看到这种情况发生很多,最常见的是在实现collect
特征的类型上使用Iter
方法。您将能够调用collect
,但除非您指定类型,否则将无法调用结果上的任何方法。
这样可行:
fn create_numbers(last_num: i32) -> Vec<i32> {
let x = (0..10).collect();
x
}
但这不是:
fn create_numbers(last_num: i32) -> Vec<i32> {
let x = (0..10).collect();
// In order to call `push`, we need to *already* know the type
// of x for "absolute certain", and the Rust compiler doesn't
// keep looking forward
x.push(42);
x
}