【问题标题】:Why does type inference not work during addition when it does during assignment为什么类型推断在添加期间不起作用,而在分配期间起作用
【发布时间】:2020-03-05 20:03:03
【问题描述】:

我正在使用 rust_decimal crate 制作应用程序。我的应用程序的一部分涉及值 '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() 在最后两行中返回的类型。这是为什么呢?

【问题讨论】:

  • 这能回答你的问题吗? How does one subtract 1 from a BigInt in Rust?
  • 总结:实现Add<Decimal, Output = Decimal>的类型可能很多,编译器不会选择一个。要修复它,最简单的方法可能是使用turbofish:let add = one::<Decimal>() + valadd 上的: Decimal 注释不是必需的,因为Add 实现明确地确定了Output 类型。)
  • 有没有办法让编译器显示它无法选择的内容? Decimal 只允许与其他 Decimal 相加(否则我将使用文字 '1' 代替),所以我不确定在这种特殊情况下的歧义是什么。

标签: rust type-inference


【解决方案1】:

只有一种类型¹可以分配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>。但同样,可能有很多Ts 满足这个约束,编译器不会选择一个。

要修复任一错误,您可以明确表示您想要oneDecimal 版本:

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

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

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

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

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


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

²编译器可以选择类型的上下文有限。一个例子见this answerAutoref-based stable specialization 描述了使用类似的技术。我找不到将其应用于您的问题的方法。

【讨论】:

    【解决方案2】:

    这就是 Rust 操作符重载的方式。 对于像a+b 这样的表达式,Rust 将首先确定a 的类型,假设它的类型为Tb 的类型为U。 当T 实现Add&lt;U, Output = V&gt; 时,可以编译加法,结果类型将为V

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

    因为one() 有多种实现Add 特性的类型,它无法确定one() 应该具有哪个类型。可能是f64 实现了Add&lt;Decimal, Output=Decimal&gt;,这会让你的表达变得模棱两可。

    在表达式val + one() 中,第一个操作数的类型已确定,但同样可以应用Add 的多个实现:Add&lt;Decimal, Output=Decimal&gt;Add&lt;f64, Output=Decimal

    所有这些都可以通过注释one() 来解决:one::&lt;Decimal&gt;()。其中有一个明确的类型。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-29
      • 2016-12-03
      • 2016-09-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多