我正在使用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
的类型,假设它的类型为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
。
所有这些都可以通过如下注释one()
来解决:one::<Decimal>()
。它有一个明确的类型。
只有一种类型可以分配给Decimal
—Decimal
本身—但是可以有任何数量的类型可以被添加到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的稳定专业化描述使用类似的技术。我找不到一种方法来解决你的问题。