为什么在分配过程中类型推断不能在加法过程中起作用

时间:2019-11-09 12:24:12

标签: rust type-inference

我正在使用rust_decimal板条箱制作应用。我的应用程序的一部分涉及值“ 1”为操作数的操作,因此我尝试使用num_traits :: identities :: one()并遇到一些意外错误:

use rust_decimal::Decimal;
use num_traits::identities::*;

fn foo(val : Decimal) {
    let _1 = one(); // E0282, expected
    let _one : Decimal = one(); // Ok, expected
    let add : Decimal = one() + val; // E0283, unexpected
    let inc : Decimal = val + one(); // E0284, unexpected
}

令我惊讶的是,编译器无法找出one()在最后两行中返回的类型。为什么会这样?

2 个答案:

答案 0 :(得分:1)

这就是Rust进行运算符重载的方式。 对于类似a+b的表达式,Rust首先确定a的类型,假设它的类型为T,而b的类型为U。 当T实现Add<U, Output = V>时,可以编译附加项,并且结果类型将为V

在某些情况下,编译器可以根据上下文推断a的类型,但在您的示例中情况并非如此。

由于one()具有实现Add特性的多种类型,因此无法确定one()应该具有哪种类型。 f64可能实现Add<Decimal, Output=Decimal>,这会使您的表达含糊。

在表达式val + one()中,确定了第一个操作数的类型,但同样可以应用Add的多个实现:Add<Decimal, Output=Decimal>Add<f64, Output=Decimal。 / p>

所有这些都可以通过注释one()来解决:one::<Decimal>()。具有明确的类型。

答案 1 :(得分:0)

只有一种类型¹可以分配DecimalDecimal本身-但可以添加任意数量的类型Decimal

let add: Decimal = one() + val;

one()必须是实现Add<Decimal, Output = Decimal> some 类型。但是可能有许多类型可以满足该约束,并且编译器不会选择一种,因此会出现错误。

let inc: Decimal = val + one();

在这种情况下,如果one()的类型为T,则Decimal必须实现Add<T, Output = Decimal>。但是同样,可能有许多T满足此约束,并且编译器不会选择一个。

要解决这两个错误,可以明确表示要使用Decimal的{​​{1}}版本:

one

let add = one::<Decimal>() + val; 上的: Decimal注释不再必要,因为add实现明确确定了Add类型。)

  

小数仅允许与其他小数相加(否则我将使用文字'1'),因此我不确定在这种情况下的歧义。

实际上有多少种类型的 满足要求并不重要。编译器不会“寻找”满足所有约束的类型。类型必须仅由本地信息明确确定。假设只有一种类型有效,但是它是在第三方包装箱中定义的-编译器是否应该知道?或者,如果只有一种类型 now 有效,但是随后您需要添加一个新的板条箱,现在有两种类型-您的代码现在应该收支平衡了吗?为了避免Rust拒绝为您选择一种类型的这种非本地破坏。一般而言,编译器只会推演类型。它不会推测。

这个问题很像How does one subtract 1 from a BigInt in Rust?


¹好吧,这并非完全正确。可以强制强制为Output的类型也可以分配给Decimal变量。但是,只有在编译器已经知道赋值的两面时,才可以进行强制转换,因此,在发生强制转换时,您无法通过Decimal进行推断。

²在有限的上下文中,编译器 可以选择类型。有关示例,请参见this answerAutoref-based stable specialization描述了使用类似的技术。我找不到解决此问题的方法。