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

use*_*784 2 generics rust

我正在尝试使用num crate Floattrait在我的库中实现泛型,但是我一直坚持与编译器作战。这有效:

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()
    }
}
Run Code Online (Sandbox Code Playgroud)

但这不是:

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()
    }
}
Run Code Online (Sandbox Code Playgroud)

后者给我以下错误:

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()
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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()
    }
}
Run Code Online (Sandbox Code Playgroud)

She*_*ter 5

让我们仔细看看这个Float特征。它定义为:

pub trait Float: NumCast + Num + Copy + Neg<Output = Self> + PartialOrd<Self> {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

深入研究此Num特征,我们看到:

pub trait Num: Zero + One + NumOps<Self, Self> + PartialEq<Self> {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

更深入 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>
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)

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

您有一个&T,正尝试将其乘以另一个&T。这是一个简化的示例:

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

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

问题是您知道可以将a乘以T另一个T。也就是说,您必须显式取消引用变量。既然Float也需要Copy,这将起作用:

let z = (*a) * (*b);
Run Code Online (Sandbox Code Playgroud)

将相同的更改应用于原始代码将使其正常工作:

for u in &self.data {
    s = s + (*u) * (*u);
}
Run Code Online (Sandbox Code Playgroud)

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

for &u in &self.data {
    s = s + u * u;
}
Run Code Online (Sandbox Code Playgroud)

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

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()
    }
}
Run Code Online (Sandbox Code Playgroud)

您还可以在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()
    }
}
Run Code Online (Sandbox Code Playgroud)

也可以看看: