generics - 为什么需要实现 Add 来添加两个具有特征 Num 的变量?

标签 generics rust

我有一个名为 add_vec 的函数.它需要两个向量并创建一个新向量,方法是对压缩向量中的一对元素执行逐元素相加。

extern crate num;
use num::traits::Num;

fn add_vec<N: Num>(v1s: Vec<N>, v2s: Vec<N>) -> Vec<N> {
    let mut v3s = Vec::new();
    for (v1, v2) in v1s.iter().zip(v2s.iter()) {
        v3s.push(v1 + v2)
    }
    v3s
}

#[cfg(test)]
mod tests {
    use super::add_vec;
    #[test]
    fn it_works() {
        let v1s = vec![1, 0, 3];
        let v2s = vec![0, 1, 1];
        let v3s = add_vec(v1s, v2s);
        assert_eq!(v3s, vec![1, 1, 4]);
    }
}

问题是我最终收到以下错误消息:

error[E0369]: binary operation `+` cannot be applied to type `&N`
  --> src/lib.rs:14:18
   |
14 |         v3s.push(v1 + v2)
   |                  ^^
   |
note: an implementation of `std::ops::Add` might be missing for `&N`
  --> src/lib.rs:14:18
   |
14 |         v3s.push(v1 + v2)
   |                  ^^

我知道这在 Requiring implementation of Mul in generic function 中得到了回答但我不明白为什么我需要实现 Add特征。好像有数不能相加...

有没有办法在不实现 Add 的情况下解决这个问题? ?我想创建 add_vec 的版本使用- , % , * , /而不是 + ,并为每个操作实现各自的特征听起来像是一件苦差事。我只对有符号整数和 float 感兴趣,所以这些 num 子类型可能存在一个特征?

我的 Cargo.toml为了方便起见:

[package]
name = "minimal_example_2"
version = "0.1.0"
authors = ["User"]

[dependencies]
num = "0.1.36"

最佳答案

要认识到的重要一点是代码并没有试图添加两个 N ;该代码试图添加两个 N 的引用 ( &N )。看报错信息(重点加了):

binary operation + cannot be applied to type &N

通过使用 iter ,您正在对向量中的值进行引用迭代。无法保证对实现特征的类型的引用实现了特征。

此外,T: Add<T, Output=T> 暗示&T: Add<&T, Output=T> .

but I do not understand why I need to implement the Add trait. As if there are numbers that cannot be added...

你为什么认为 N 是一个数字Rust 中存在的任何可能类型 都欢迎实现任何特征,包括 Num .如果有意义,您可以实现 Num适合您自己的类型。

perhaps there exists a trait for these subtypes of num?

同样,一旦存在特征,任何类型都欢迎实现它。


对于它的值(value),我会像这样实现它:

use std::ops::Add;

fn add_vec<'a, N>(v1s: &'a [N], v2s: &'a [N]) -> Vec<N>
    where &'a N: Add<Output = N>
{
    v1s.iter().zip(v2s.iter()).map(|(v1, v2)| v1 + v2).collect()
}

参见 Why is it discouraged to accept a reference to a String (&String) or Vec (&Vec) as a function argument? (此外,我们只是需要在这里取得所有权)。使用 map会更有效率——它可以一次预分配所有输出向量。

但是,如果您真的想使用向量:

fn add_vec<N: Num>(v1s: Vec<N>, v2s: Vec<N>) -> Vec<N> {
    v1s.into_iter()
        .zip(v2s.into_iter())
        .map(|(v1, v2)| v1 + v2)
        .collect()
}

请注意 into_iter消耗向量,产生值而不是引用。因此 Num可以应用特征。

关于generics - 为什么需要实现 Add 来添加两个具有特征 Num 的变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40241159/

相关文章:

rust - 使用Serde反序列化变量元对象

java - Java 中的方法覆盖和泛型问题

java - 当使用 Bounded 类型参数或直接类型接口(interface)时

java - 如何使对象具有可比性

rust - 如何将计算9位位掩码中1的个数的C函数转换为Rust?

rust - 创建 Vec 时,借用的值不够长

java - Intellij 给出了奇怪的错误。 Java 泛型问题

c# - List<List<int>>的组合

pointers - `*const T` 和 *mut T` 原始指针有什么区别?

rust - 如何根据其中一个Vecs的值对两个Vecs进行联合排序?