3

I'm trying to use the sprs crate (version 0.6.3) to manipulate sparse vectors. I'd like to add two vectors together. I started off with an implementation of the Add trait, then simplified this to an implementation function. Finally, I've boiled down the problem to a simple generic function.

// This works: the scalar type `u64` is fixed here
fn adder(first: &CsVec<u64>, second: &CsVec<u64>) -> CsVec<u64> {
    first + second
}

// When I try to make the scalar type generic, it doesn't work
fn adder2<T>(first: &CsVec<T>, second: &CsVec<T>) -> CsVec<T>
where
    CsVec<T>: Add,
    T: Add + Debug,
{
    first + second
}

The first version compiles fine, but I'd like to know why the second version won't compile. I get this error message:

error[E0369]: binary operation `+` cannot be applied to type `&sprs::sparse::CsVecBase<std::vec::Vec<usize>, std::vec::Vec<T>>`
  --> libp3prime/src/lib/datacache.rs:62:5
   |
62 |     first + second
   |     ^^^^^^^^^^^^^^
   |
   = note: an implementation of `std::ops::Add` might be missing for `&sprs::sparse::CsVecBase<std::vec::Vec<usize>, std::vec::Vec<T>>`

I don't really understand the error message. I know that you can add two CsVecs together, since adder() compiles, so I am a bit lost.

The two vectors should add together.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
garypen
  • 91
  • 1
  • 6
  • 1
    @E_net4 OP mentioned [sprs crate where CsVec structure is defined](https://docs.rs/sprs/0.2.0/sprs/sparse/vec/struct.CsVec.html) – Alex Yu Feb 15 '19 at 14:13
  • 2
    I believe the question does meet the MCVE standards. I did note that CsVec was part of the sprs crate (someone has kindly edited my question to add a link to the crate) and expected that anyone wanting to know the implementation of CsVec would look at the sprs documentation. Apart from that, I believed that all the information required was present. – garypen Feb 15 '19 at 14:15
  • After answering, I found this question: [How do I require a generic type implement an operation like Add, Sub, Mul, or Div in a generic function?](https://stackoverflow.com/q/29184358/3650362) Is this close enough to flag as a duplicate? – trent Feb 15 '19 at 14:28
  • @trentcl I did search and failed to find that. That is close enough to flag as duplicate. – garypen Feb 15 '19 at 14:41

1 Answers1

4

Be sure that the trait bounds defined on the function match the behavior used in the function.

first and second are not CsVec<T>, but &CsVec<T>. In Rust, &X is a different type from X. You need a trait bound that says that you can add two &CsVec<T>s and get a CsVec<T> as output:

fn adder2<'a, T>(first: &'a CsVec<T>, second: &'a CsVec<T>) -> CsVec<T>
where
    &'a CsVec<T>: Add<Output = CsVec<T>>,
{
    first + second
}

No bounds on T are needed in this example.

The 'a lifetime parameter in this case was passed in to the function. Sometimes it is useful to define a trait bound on a reference inside the function, for example, to use + on references to local variables. In that case you would want to use the higher-ranked trait bound for<'a> &'a CsVec<T>: Add<Output = CsVec<T>> instead. See the linked questions below for more information.

Lukas Kalbertodt points out that it may sometimes be more flexible to say "I just want to add two &CsVec<T>s, and I will return whatever type that operation gives me", which you can do by returning <&'a CsVec<T> as Add>::Output:

fn adder2<'a, T>(first: &'a CsVec<T>, second: &'a CsVec<T>) -> <&'a CsVec<T> as Add>::Output
where
    &'a CsVec<T>: Add,
{
    first + second
}

In this case the output type does not have to be exactly CsVec<T>, but when it is, it works the same way as the first version.

Related

trent
  • 25,033
  • 7
  • 51
  • 90
  • @LukasKalbertodt: Thanks to both of you! That makes lifetimes and their usage a lot clearer for me. – garypen Feb 15 '19 at 14:44