Rust如何解决Hindley-Milner的可变性?

Pet*_*efi 4 mutable hindley-milner rust

我读过Rust在使用Hindley-Milner时具有很好的类型推断。Rust还具有可变变量,并且当HM算法具有可变性时,AFAIK必须存在一些约束,因为它可能过于笼统。如下代码:

let mut a;
a = 3;
a = 2.5;
Run Code Online (Sandbox Code Playgroud)

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

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

let mut v;
v = Vec::new();
v.push(3);
v.push(2.3);
Run Code Online (Sandbox Code Playgroud)

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

怎么了 有我不知道的诸如价值限制之类的东西吗?还是我使事情复杂化了,Rust有更严格的规则(就像根本没有泛化一样)?

Net*_*ave 5

如果我没看错,它会这样做:

let mut a;
a = 3;     //here a is already infered as mut int
a = 2.5;   //fails because int != float
Run Code Online (Sandbox Code Playgroud)

对于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>
Run Code Online (Sandbox Code Playgroud)

请注意,我没有使用锈类型,只是为了有一个一般的想法。

  • @PeterLenkefi要记住,Rust没有数字的类型层次结构,因此Rust不能简单地使其成为“ Vec &lt;Number&gt;”或“ Vec &lt;Rational&gt;”之类的东西,这一点很重要。它能做的最接近的可能是`Vec &lt;&PartialOrd&gt;`,但是创建[trait objects](https://doc.rust-lang.org/book/trait-objects.html)会产生运行时成本,Rust试图使运行时成本明确。 (2认同)

Mat*_* M. 5

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

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

let mut a = 3;
a = 2.5;
Run Code Online (Sandbox Code Playgroud)

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

预计更好的算法会记录冲突,然后指向每种类型来自的行。也许我们会用Chalk做到这一点。

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


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

let mut v = Vec::new();
v.push(3);
Run Code Online (Sandbox Code Playgroud)

详细说明:

  • 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 };
Run Code Online (Sandbox Code Playgroud)

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