She*_*ter 30 type-systems type-inference hindley-milner rust
此代码编译:
#[derive(Debug, Default)]
struct Example;
impl Example {
fn some_method(&self) {}
}
fn reproduction() -> Example {
let example = Default::default();
// example.some_method();
example
}
Run Code Online (Sandbox Code Playgroud)
如果添加了注释行,则会导致错误:
#[derive(Debug, Default)]
struct Example;
impl Example {
fn some_method(&self) {}
}
fn reproduction() -> Example {
let example = Default::default();
// example.some_method();
example
}
Run Code Online (Sandbox Code Playgroud)
为什么添加此方法调用会导致类型推断失败?
我看过以下两个问题:
从他们那里,我知道Rust使用的是Hindley-Milner 的(修改的)版本。后一个问题的答案将Rust的类型推论描述为一个方程系统。另一个答案明确指出“ Rust中的类型信息可以倒流”。
使用适用于这种情况的知识,我们可以:
example 是类型 ?E?E 必须有一个称为 some_method?E 返回Example向后工作,很容易使人看到?E必须如此Example。我所看到的与编译器所看到的之间的差距在哪里?
zrz*_*zka 20
根据已知事实(请参阅下文),它无法编译,因为:
let example = Default::default();,example可以是任何实现的东西Default,Default”都不是已知类型。我替换some_method()为字段访问,它产生相同的错误。
Run Code Online (Sandbox Code Playgroud)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
Run Code Online (Sandbox Code Playgroud)
您应该关注eddyb(自2016年5月以来成为Rust语言设计团队的知名成员)的以下评论。
评论1:
这是顺序类型检查器的已知限制。尽管推理
thing.f1.clone()是自由流动的,但请先 进行检查,things.push(Thing {...})以便thing: Thing在尝试访问该f1字段时不知道。我们将来可能会放弃这一点,但没有立即的计划。
更重要的是评论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
}
Run Code Online (Sandbox Code Playgroud)
但这不是:
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
}
Run Code Online (Sandbox Code Playgroud)