我正在使用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()在最后两行中返回的类型。为什么会这样?
答案 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)
只有一种类型¹可以分配到Decimal
- Decimal
本身-但可以添加任意数量的类型到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 answer。 Autoref-based stable specialization描述了使用类似的技术。我找不到解决此问题的方法。