1

I have a function called add_vec. It takes two vectors and creates a new one, by performing an elementwise add on the pair of elements from the zipped vectors.

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]);
    }
}

The problem is that I end up with the following error message:

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)
   |                  ^^

I know this is answered in Requiring implementation of Mul in generic function but I do not understand why I need to implement the Add trait. As if there are numbers that cannot be added...

Is there a way to solve this without implementing Add? I want to create a version of add_vec that uses -, %, *, / instead of +, and implementing the respective traits for each operation sounds like a drudge. I'm only interested in signed integers and floats, so perhaps there exists a trait for these subtypes of num?

My Cargo.toml for convenience:

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

[dependencies]
num = "0.1.36"
Community
  • 1
  • 1
The Unfun Cat
  • 29,987
  • 31
  • 114
  • 156

1 Answers1

6

The important thing to realize is that the code isn't attempting to add two Ns; the code is attempting to add two references to N (&N). Look at the error message (emphasis added):

binary operation + cannot be applied to type &N

By using iter, you are iterating on references to values inside the vector. There's no guarantee that a trait is implemented for a reference to a type that implements the trait.

In addition, T: Add<T, Output=T> does not imply &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...

Why do you assume that N is a number? Any possible type that exists in Rust is welcome to implement any trait, and that includes Num. If it made sense, you could implement Num for your own type.

perhaps there exists a trait for these subtypes of num?

Again, once a trait exists, any type is welcome to implement it.


For what it's worth, I'd implement it like this:

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()
}

See Why is it discouraged to accept a reference to a String (&String) or Vec (&Vec) as a function argument? (also we just don't need to take ownership here). Using map is going to be more efficient - it can preallocate the output vector all at once.

However, if you really wanted to consume the vectors:

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()
}

Note that into_iter consumes the vectors, yielding values and not references. Thus the Num trait can be applied.

Community
  • 1
  • 1
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 2
    But according to http://rust-num.github.io/num/num/trait.Num.html, `Num: ...+ Add + ...`, which you might think means you can assume `Add`. – Chris Emerson Oct 25 '16 at 13:31
  • @ChrisEmerson yes, you can add together two values of a type that implements `Num` (and thus `Add`), but *not* two references to values of a type that implement `Num`. `T: Add` does not imply `&T: Add<&T, Output=T>`. – Shepmaster Oct 25 '16 at 13:38
  • Thanks for your great answer. What is the `'a` and the `Output` in the signature called? Want to read up on them further. – The Unfun Cat Oct 25 '16 at 18:03
  • @TheUnfunCat [a lifetime](https://doc.rust-lang.org/stable/book/lifetimes.html) and [an associated type](https://doc.rust-lang.org/stable/book/associated-types.html), respectively. I'd **highly** recommend reading the [**entire** book](https://doc.rust-lang.org/stable/book/); it has the collected answers to many questions that people ask when starting Rust. – Shepmaster Oct 25 '16 at 19:04