在使用泛型进行算术运算时,不能应用二进制运算



我正试图使用num crate Float特性在我的库中实现泛型,但我一直在与编译器作斗争。这项工作:

struct Vector<T> {
    data: Vec<T>,
}
trait Metric<T> {
    fn norm(&self) -> T;
}
impl Metric<f32> for Vector<f32> {
    fn norm(&self) -> f32 {
        let mut s = 0.0;
        for u in &self.data {
            s = s + u * u;
        }
        s.sqrt()
    }
}

但事实并非如此:

use num::Float; // 0.2.0
struct Vector<T> {
    data: Vec<T>,
}
trait Metric<T> {
    fn norm(&self) -> T;
}
impl<T: Float> Metric<T> for Vector<T> {
    fn norm(&self) -> T {
        let mut s = T::zero();
        for u in &self.data {
            s = s + u * u;
        }
        s.sqrt()
    }
}

后者给了我以下错误:

error[E0369]: binary operation `*` cannot be applied to type `&T`
  --> src/lib.rs:16:23
   |
16 |             s = s + u * u;
   |                     - ^ - &T
   |                     |
   |                     &T
   |
   = note: an implementation of `std::ops::Mul` might be missing for `&T`

如果我删除引用并迭代self.data,我会得到一个

error[E0507]: cannot move out of borrowed content
  --> src/lib.rs:15:18
   |
15 |         for u in self.data {
   |                  ^^^^^^^^^ cannot move out of borrowed content

让我们更深入地了解Float特性。它被定义为:

pub trait Float: NumCast + Num + Copy + Neg<Output = Self> + PartialOrd<Self> {
    // ...
}

深入到Num特性,我们可以看到:

pub trait Num: Zero + One + NumOps<Self, Self> + PartialEq<Self> {
    // ...
}

并深入NumOps

pub trait NumOps<Rhs = Self, Output = Self>:
    Add<Rhs, Output = Output>
    + Sub<Rhs, Output = Output>
    + Mul<Rhs, Output = Output>
    + Div<Rhs, Output = Output>
    + Rem<Rhs, Output = Output>
{
    // ...
}

这意味着任何实现Float的类型都可以乘以其自己的类型。现在让我们回到您的代码。您正在对一个Vec<T>进行迭代,它为您提供了对每个项目的引用,即&T

您有一个&T,并试图将其乘以另一个&T。这里有一个简化的例子:

fn do_a_thing<T>(a: &T, b: &T)
where
    T: Float,
{
    let z = a * b;
}

这给出了相同的错误:binary operation `*` cannot be applied to type `&T`

问题是,您只有知道可以将一个T乘以另一个T。要做到这一点,您必须明确地取消引用变量。由于Float也需要Copy,这将起作用:

let z = (*a) * (*b);

对原始代码应用相同的更改会使其工作:

for u in &self.data {
    s = s + (*u) * (*u);
}

您还可以在模式匹配时取消引用迭代器变量:

for &u in &self.data {
    s = s + u * u;
}

或者,您可以添加另一个绑定,该绑定要求对您的类型的引用可以相乘:

impl<T> Metric<T> for Vector<T>
where
    T: Float,
    for<'a> &'a T: std::ops::Mul<&'a T, Output = T>,
{
    fn norm(&self) -> T {
        let mut s = T::zero();
        for u in &self.data {
            s = s + u * u;
        }
        s.sqrt()
    }
}

您还可以添加AddAssign的绑定,并在正文中编写更简单的代码:

impl<T> Metric<T> for Vector<T>
where
    T: Float + std::ops::AddAssign,
    for<'a> &'a T: std::ops::Mul<&'a T, Output = T>,
{
    fn norm(&self) -> T {
        let mut s = T::zero();
        for u in &self.data {
            s += u * u;
        }
        s.sqrt()
    }
}

另请参阅:

  • 如何编写为添加两个泛型类型的引用而绑定的特性
  • 当其中一个引用是本地引用时,如何在类型约束中编写引用的生存期
  • 对于<gt;语法与常规的终身绑定不同

最新更新