首页 文章

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

提问于
浏览
2

我已经读过Rust使用Hindley-Milner进行非常好的类型推断 . Rust也有可变变量和AFAIK,当HM算法使用可变性时必须有一些约束,因为它可能过度泛化 . 以下代码:

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

不编译,因为在第二行推断出整数,并且不能将浮点值赋给整数变量 . 所以我猜测对于简单变量,只要推断出非泛型类型,变量就变成单一类型,不能再进行泛化 .

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

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

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

规则是什么?是否存在我不知道的 Value 限制?或者我过于复杂的事情和Rust有更严格的规则(比如根本没有概括)?

2 回答

  • 2

    它被认为是一个问题(就诊断质量而言),rustc在其类型推断中略显过于急切 .

    如果我们检查你的第一个例子:

    let mut a = 3;
    a = 2.5;
    

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

    预计更好的算法将记录冲突,然后指向每种类型来自的行 . 也许我们会用Chalk得到它 .

    注意:泛型整数类型是Rust的一个技巧,可以生成整数文字"polymorphic",如果没有其他提示应该是什么特定的整数类型,它将默认为 i32 .


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

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

    详情如下:

    • v 已分配类型 $T

    • Vec::new() 生成类型 Vec<$U>

    • 3 生成类型 {integer}

    所以,在第一行,我们得到 $T == Vec<$U> ,在第二行我们得到 $U == {integer} ,因此 v 被推断为具有类型 Vec<{integer}> .

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


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

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

    关于HM,Rust有更糟糕的问题,并且子类型变得更具挑战性 .

  • 4

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

    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>
    

    请注意,我没有使用防锈类型,只是为了有一个大致的想法 .

相关问题