【问题标题】:Binary operation cannot be applied when using generics for arithmetic使用泛型进行算术运算时不能应用二元运算
【发布时间】:2016-03-13 16:32:37
【问题描述】:

我正在尝试使用num crate Float trait 在我的库中实现泛型,但我一直在与编译器作斗争。这有效:

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

【问题讨论】:

    标签: generics rust


    【解决方案1】:

    让我们仔细看看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&lt;T&gt;,它为您提供了对每个项目的引用,即 &amp;T

    您有一个&amp;T,并试图将它与另一个&amp;T 相乘。这是一个简化的例子:

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

    这给出了同样的错误:binary operation `*` cannot be applied to type `&amp;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()
        }
    }
    

    另见:

    【讨论】:

    • 感谢您澄清这一点 - 了解为什么会这样真的很有帮助。至于 += 部分,我假设不可能将此运算符用于泛型,我是否正确?
    • @user124784 not yet,但它来了。 Specifying good semantics 因为那个操作员比你想象的更难:-)
    • 我可以等 :)。只是检查一下 - 编译器的优化不会看到这些之间有任何区别吗?
    猜你喜欢
    • 2018-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-27
    • 1970-01-01
    • 1970-01-01
    • 2019-03-25
    • 2023-03-29
    相关资源
    最近更新 更多