为什么在变量上调用方法会阻止 Rust 推断变量的类型? [英] Why does calling a method on a variable prevent Rust from inferring the type of the variable?
问题描述
此代码编译:
#[derive(Debug, Default)]结构体示例;impl 示例 {fn some_method(&self) {}}fn 复制()->例子 {让示例 = Default::default();//example.some_method();例子}
如果把注释行加回来,会报错:
error[E0282]:需要类型注释-->src/lib.rs:10:5|9 |让示例 = Default::default();|------- 考虑给 `example` 一个类型10 |例子.some_method();|^^^^^^^ 无法推断类型|= 注意:此时必须知道类型
为什么添加这个方法调用会导致类型推断失败?
我看过这两个问题:
从他们那里,我知道 Rust 使用了一个 (修改的) Hindley-Milner 版本.后一个问题有答案,将 Rust 的类型推断描述为方程组.另一个答案明确指出Rust 中的类型信息可以倒流".
将这些知识应用于这种情况,我们有:
example
是类型?E
?E
必须有一个名为some_method
的方法?E
返回- 返回类型为
Example
向后工作,人们很容易看到?E
必须是Example
.我能看到的和编译器能看到的差距在哪里?
根据已知事实(见下文),它无法编译,因为:
- 类型检查器按照函数的编写顺序,
- 在
let example = Default::default();
中,example
可以是任何实现Default
的东西, - 现场访问&方法调用需要已知类型,
- 任何实现
Default
"的东西都不是已知类型.
我用一个字段访问替换了 some_method()
并且它产生了同样的错误.
来自 类型推断取决于排序 (#42333)::><块引用>
使用 std::path::PathBuf;酒吧结构东西{pub f1: PathBuf,}fn 垃圾()->Vec<事物>{让 mut things = Vec::new();对于 vec 中的 x![1, 2, 3] {如果 x == 2 {对于things.drain(..) {thing.f1.clone();}返回 vec![]}things.push(Thing{f1: PathBuf::from(format!("/{}", x))});}事物}fn 主(){垃圾();}
这会在 Rust 1.33.0 中产生编译器错误:
error[E0282]:需要类型注释-->src/main.rs:13:17|9 |让 mut things = Vec::new();|---------- 考虑给 `things` 一个类型...13 |thing.f1.clone();|^^^^^ 无法推断类型|= 注意:此时必须知道类型
您应该关注 eddyb(Rust 语言设计的知名成员)的以下评论团队 自 2016 年 5 月).
<块引用>这是有序类型检查器的已知限制.当推理自由流动时,thing.f1.clone()
before things.push(Thing {...})
所以当您尝试访问 f1
字段时,thing: Thing
是未知的.我们将来可能会远离这一点,但目前还没有计划.
更重要的是评论#2:
<块引用>我的意思是类型检查器按照函数的编写顺序遍历函数.[...] 除非类型已知,否则根本不支持字段访问和方法调用.
This code compiles:
#[derive(Debug, Default)]
struct Example;
impl Example {
fn some_method(&self) {}
}
fn reproduction() -> Example {
let example = Default::default();
// example.some_method();
example
}
If the commented line is added back, it will cause an error:
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
Why does adding this method call cause type inference to fail?
I've seen these two questions:
- How does Rust's type inference work across multiple statements?
- How does Rust infer resultant types from From::<>::from()?
From them, I know that Rust uses a (modified) version of Hindley-Milner. The latter question has an answer that describes Rust's type inference as a system of equations. Another answer explicitly states that "Type information in Rust can flow backwards".
Using this knowledge applied to this situation, we have:
example
is type?E
?E
must have a method calledsome_method
?E
is returned- The return type is
Example
Working backward, it's easy for a human to see that ?E
must be Example
. Where is the gap between what I can see and what the compiler can see?
Based on known facts (see below), it fails to compile because:
- the type checker goes through the function in the order it was written,
- in
let example = Default::default();
,example
can be anything which implementsDefault
, - field accesses & method calls require a known type,
- "anything implementing
Default
" is not a known type.
I replaced some_method()
with a field access and it produces same error.
From 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(); }
This produces a compiler error with 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
You should focus on the following comments from 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.
What's more important is 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.
这篇关于为什么在变量上调用方法会阻止 Rust 推断变量的类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!