6

我读过 Rust 使用 Hindley-Milner 具有非常好的类型推断。Rust 也有可变变量,并且当 HM 算法与可变性一起工作时,AFAIK 必须有一些约束,因为它可能过度概括。以下代码:

let mut a;
a = 3;
a = 2.5;

不编译,因为在第二行推断出整数并且不能将浮点值分配给整数变量。所以我猜测对于简单的变量,一旦推断出非泛型类型,该变量就会变成单类型并且不能再被泛化。

但是像 Vec 这样的模板呢?例如这段代码:

let mut v;
v = Vec::new();
v.push(3);
v.push(2.3);

这再次失败,但最后一行再次失败。这意味着第二行部分推断出类型(Vec),第三行推断出容器类型。

有什么规律?是否有我不知道的价值限制之类的东西?还是我把事情复杂化了,而 Rust 有更严格的规则(就像根本没有概括一样)?

4

2 回答 2

5

如果我没记错的话,它会这样做:

let mut a;
a = 3;     //here a is already infered as mut int
a = 2.5;   //fails because int != float

对于 vec 片段:

let mut v;
v = Vec::new();// now v type is Vec<something>
v.push(3);     // v type is Vec<int>
v.push(2.3);   // here fails because Vec<int> != Vec<float>

请注意,我没有使用 rust 类型,只是为了大致了解一下。

于 2017-05-10T08:19:59.057 回答
5

rustc 在其类型推断中有点过于急切,这被认为是一个问题(就诊断质量而言)。

如果我们检查您的第一个示例:

let mut a = 3;
a = 2.5;

然后第一行导致推断a具有 a {generic integer type},第二行将导致2.5无法分配的诊断,a因为它不是通用整数类型。

预计会有更好的算法代替记录冲突,然后指向每种类型的来源。也许我们会用Chalk得到它。

注意:通用整数类型是 Rust 使整数文字“多态”的一个技巧,如果没有其他提示说明它应该是什么特定整数类型,它将默认为i32.


第二个例子的发生方式基本相同。

let mut v = Vec::new();
v.push(3);

详细说明:

  • v被分配类型$T
  • Vec::new()产生类型Vec<$U>
  • 3产生类型{integer}

所以,在第一行,我们得到$T == Vec<$U>,在第二行,我们得到$U == {integer},所以v推断有 type Vec<{integer}>

如果没有其他来源可以了解确切的整数类型,则i32默认情况下会回退。


我想指出,可变性实际上并不影响这里的推理。从类型推断或类型统一的角度来看,以下代码示例是等价的:

//  With mutability:
let mut a = 1;
a = 2.5;

//  Without mutability:
let a = if <condition> { 1 } else { 2.5 };

Rust 中关于 HM 的问题要严重得多,Deref子类型也更具挑战性。

于 2017-05-10T11:01:16.397 回答