首页 > 解决方案 > 尽管有限制,Rust `cannot infer type`

问题描述

我想编写一段代码,它可以采用可复制类型的引用或拥有的值,并返回该类型的拥有版本。我已将类型推断的问题减少到以下代码,其中错误:

use std::borrow::Borrow;

fn copy<R, E>(val: E) -> R
where
    R: Default + Copy,
    E: Borrow<R>,
{
    *val.borrow()
}

fn main() {
    assert_eq!(6, copy(&6));
    assert_eq!(6, copy(6));
    assert_eq!(6.0, copy(&6.0));
    assert!((6.0f64 - copy(&6.0f64)).abs() < 1e-6);
}

错误来自最后一个断言:

error[E0282]: type annotations needed
  --> src/main.rs:15:13
   |
15 |     assert!((6.0f64 - copy(&6.0f64)).abs() < 1e-6);
   |             ^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
   |
   = note: type must be known at this point

我唯一的假设是允许orSub上的特征,如果不存在约束,那么最后一个副本的有效表达式将是,但是这是不允许的,因为没有实现。如果我们通过值传递它会起作用,大概是因为它限制为 be而不是两者之一。f64f64&f64Defaultcopy::<&f64, &f64>(&6.0f64)&f64Defaultf64Rf64

我不清楚的是为什么编译器不能进一步限制 的返回类型copy,或者如何向编译器指示返回的值不是引用。

标签: rust

解决方案


没有任何东西copy限制R特定的具体类型。特别是,&f64 可以实现(不仅仅是)Borrow<R>的多个值。它不在您当前的代码中,但缺乏替代方案不被视为选择特定实现的理由。Rf64

我什至可以添加一个匹配的实现:

#[derive(Copy, Clone, Debug, Default)]
struct Foo;

impl Borrow<Foo> for &f64 {
    fn borrow(&self) -> &Foo { &Foo }
}

(即使f64是标准库类型,也允许此 trait 实现,因为Foo它是在当前 crate 中定义的类型。)

现在我们可以实际使用选择:

fn main() {
    dbg!(copy::<f64, _>(&1.0));
    dbg!(copy::<Foo, _>(&1.0));
}
[src/main.rs:19] copy::<f64, _>(&1.0) = 1.0
[src/main.rs:20] copy::<Foo, _>(&1.0) = Foo

当返回类型实际上取决于参数类型时, like 函数copy只能从其参数类型派生其返回类型:例如,如果它是由参数实现的关联类型的 trait。两者都有一个类型参数而不是关联类型(因此可以为同一个实现类型多次实现);有一个关联类型,但不提供 from to 。您可以为此实现自己的特征:AsRefBorrowDerefTargetDereff64f64

trait DerefCopy: Copy {
    type Output;
    fn deref_copy(self) -> Self::Output;
}

impl<T: Copy> DerefCopy for &T {
    type Output = T;
    fn deref_copy(self) -> T {
        *self
    }
}

impl DerefCopy for f64 {
    type Output = Self;
    fn deref_copy(self) -> Self {
        self
    }
}

fn main() {
    assert_eq!(6, (&6).deref_copy());
    assert_eq!(6, (6).deref_copy());
    assert_eq!(6.0, (&6.0).deref_copy());
    assert!((6.0f64 - (&6.0f64).deref_copy()).abs() < 1e-6);
}

但是,这将要求您为希望使用它DerefCopy每个非引用类型实现,因为不可能为所有非引用 T编写一揽子实现;Borrow 可以有一个全面实现的原因是impl Borrow<T> for T不冲突,impl Borrow<T> for &T因为如果我们假设 T 本身是一个引用&U,我们得到impl Borrow<&U> for &&U的仍然不一样impl Borrow<T> for T


推荐阅读