为什么类型推理在加法过程中不起作用,而在赋值过程中起作用



我正在使用rust_decimal机箱制作一个应用程序。我的应用程序的一部分涉及值"1"是操作数的操作,所以我尝试使用num_traits::identitys::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((。为什么会这样?

Rust就是这样进行运算符重载的。对于像a+b这样的表达式,Rust将首先确定a的类型,假设它的类型为Tb的类型为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

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

只有一种类型可以分配DecimalDecimal本身—但是可以有任何数量的类型可以被添加到CCD_ 23。

let add: Decimal = one() + val;

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

let inc: Decimal = val + one();

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

要修复任何一个错误,您可以明确表示您想要one:的Decimal版本

let add = one::<Decimal>() + val;

(不再需要add上的: Decimal注释,因为Add实现明确地确定了Output类型。(

Decimal只允许与其他Decimal相加(否则我会使用文字"1"(,所以我不确定在这种特殊情况下的歧义是什么。

实际上,存在多少满足需求的类型并不重要。编译器不会"去寻找"满足所有约束的类型;必须仅通过本地信息来明确地确定类型。假设只有一种类型有效,但它是在第三方机箱中定义的;编译器应该知道吗?或者,如果今天只有一种类型有效,但明天你会有一个新的板条箱,并且有两种这样的类型—那么你的代码应该中断吗?为了避免这种非局部破坏,Rust拒绝为您选择类型。在一般²中,编译器只会推导出类型;它没有推测。

这个问题很像Rust中如何从BigInt中减去1?


好吧,这不是完全正确的。可以强制Decimal的类型也可以分配给Decimal变量。但是,只有当编译器已经知道赋值的双方时,强制才有可能,因此无法通过=推断强制何时发生。

²编译器可以选择类型的上下文有限。请参阅以下答案中的一个示例。基于Autoref的稳定专业化描述使用类似的技术。我找不到一种方法来解决你的问题。

最新更新