为什么在变量上调用方法会阻止 Rust 推断变量的类型?
Why does calling a method on a variable prevent Rust from inferring the type of the variable?
此代码编译:
#[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
为什么添加此方法调用会导致类型推断失败?
我看到了这两个问题:
- How does Rust's type inference work across multiple statements?
从他们那里,我知道 Rust 使用 (modified) version of Hindley-Milner. The latter question has that describes Rust's type inference as a system of equations. 明确指出 "Type information in Rust can flow backwards".
将这些知识应用于这种情况,我们有:
example
是类型 ?E
?E
必须有一个名为 some_method
的方法
?E
是 returned
- return类型是
Example
逆向计算,人类很容易看出 ?E
必须是 Example
。我能看到的和编译器能看到的差距在哪里?
我不知道完整的答案,我几乎不了解 Rust 编译器的内部工作原理,但这里有一些我从 Rust 经验中得出的推论。
关于 Rust 类型的信息可以 "flow backwards",但在某些时候 Rust 需要知道(绝对确定)表达式的类型。在这些情况下,它必须"already"知道类型,即它不会继续向前看。
据我所见,这种情况仅限于方法调用。我怀疑这与可以在特征上实现方法这一事实有关,这使事情变得非常复杂。我怀疑名为 some_method
的方法在作用域中是否存在任何特征,但我认为每当 Rust 编译器遇到方法调用时,它都要求类型已经确定。
你可以看到这发生了 lot 对实现特征的类型的方法调用,最常见的是 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
}
根据已知事实(见下文),它无法编译,因为:
- 类型检查器按照函数编写的顺序检查函数,
- 在
let example = Default::default();
、example
中可以是任何实现Default
、 的东西
- 字段访问和方法调用需要已知类型,
- "anything implementing
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 (a well-known member of the the Rust language design team since May, 2016 中的以下评论。
This is a known limitation of the in-order type-checker. While inference flows freely, thing.f1.clone()
is checked before things.push(Thing {...})
so it isn't known that thing: Thing
when you try to access the f1
field. We may in the future move away from this, but there are no immediate plans.
更重要的是comment #2:
What I mean is that the type-checker goes through the function in the order it was written. [...] Fields accesses and methods calls are simply not supported unless the type is already known.
此代码编译:
#[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
为什么添加此方法调用会导致类型推断失败?
我看到了这两个问题:
- How does Rust's type inference work across multiple statements?
从他们那里,我知道 Rust 使用 (modified) version of Hindley-Milner. The latter question has
将这些知识应用于这种情况,我们有:
example
是类型?E
?E
必须有一个名为some_method
的方法
?E
是 returned- return类型是
Example
逆向计算,人类很容易看出 ?E
必须是 Example
。我能看到的和编译器能看到的差距在哪里?
我不知道完整的答案,我几乎不了解 Rust 编译器的内部工作原理,但这里有一些我从 Rust 经验中得出的推论。
关于 Rust 类型的信息可以 "flow backwards",但在某些时候 Rust 需要知道(绝对确定)表达式的类型。在这些情况下,它必须"already"知道类型,即它不会继续向前看。
据我所见,这种情况仅限于方法调用。我怀疑这与可以在特征上实现方法这一事实有关,这使事情变得非常复杂。我怀疑名为 some_method
的方法在作用域中是否存在任何特征,但我认为每当 Rust 编译器遇到方法调用时,它都要求类型已经确定。
你可以看到这发生了 lot 对实现特征的类型的方法调用,最常见的是 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
}
根据已知事实(见下文),它无法编译,因为:
- 类型检查器按照函数编写的顺序检查函数,
- 在
let example = Default::default();
、example
中可以是任何实现Default
、 的东西
- 字段访问和方法调用需要已知类型,
- "anything implementing
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 (a well-known member of the the Rust language design team since May, 2016 中的以下评论。
This is a known limitation of the in-order type-checker. While inference flows freely,
thing.f1.clone()
is checked beforethings.push(Thing {...})
so it isn't known thatthing: Thing
when you try to access thef1
field. We may in the future move away from this, but there are no immediate plans.
更重要的是comment #2:
What I mean is that the type-checker goes through the function in the order it was written. [...] Fields accesses and methods calls are simply not supported unless the type is already known.