Use add fn inside of a function with Generic Add trait argument

Hi.

I’ve been working on a generic function so different types that implement differently the same traits can call it, since the behavior of this exact function is shared by the types.

To put an example:


pub(self) trait Double {
    type Output;

    #[must_use]
    /// Performs the point-doubling operation over the
    /// coordinates which this trait has been implemented
    /// for.
    fn double(self) -> Self::Output;
}

/// This struct implements: `Add`, `Identity`, `Double` and `Clone`.
pub struct A { /*fields*/};

/// This struct implements: `Add`, `Identity`, `Double` and `Clone`.
pub struct B { /*fields*/};

Add, Identity and Double are implemented differently for both structs.
Now I want to define a function that gets a gen argument (which can be one of both
declared above and executes the same code when an object of type A or B calls it.

pub fn double_and_add<'b, T>(point: T, scalar: &'b Scalar) -> T 
    where T: Add + Identity + Double + Clone {

    let mut N: T = point.clone();
    let mut n = scalar.clone();
    let mut Q: T = T::identity();

    while n != Scalar::zero() {
        if !n.is_even() {
            Q = &Q + &N;
        };

        N = N.double();
        n = n.half();
    }  
    Q
}

So at this point, I’m getting the following error:

mismatched types
expected type parameter, found associated type
note: expected type `T`
         found type `<T as std::ops::Add>::Output`rustc(E0308)
edwards.rs(45, 17): expected type parameter, found associated type

I understand that the result of the addition implementation has the type

<T as std::ops::Add>::Output

but since the addition implementation is as declared down, i was expecting to get the type T as the result.

// Add impl for A and B:
impl<'a, 'b> Add<&'b EdwardsPoint> for &'a A {
    type Output = A;
    /// Add two EdwardsPoints and give the resulting `EdwardsPoint`.
    /// This implementation is specific for curves with `a = -1` as Doppio is.
    /// [Source: 2008 Hisil–Wong–Carter–Dawson], 
    /// (http://eprint.iacr.org/2008/522), Section 3.1.
    #[inline]
    fn add(self, other: &'b A) -> A {
         // Implementation-....
   }
}

So at this point, I don’t know how I can deal with that. Anyone has any ideas? I know I can implement this differently, but I wanted to try to do it like this, so I want to solve the problem, not implement that on a complete different way since I can do it by myself.

Thank you so much.

Change the bound on T from Add + ... to Add<Output = T> + ...

1 Like

Thanks. It worked, now it jumped on something that I can’t catch.

The function looks like:

pub fn double_and_add<'b, 'a, T>(point: &'a T, scalar: &'b Scalar) -> T 
    where &'a T: Add<Output = T>  + Double<Output = T>,
    T: Identity + Clone {

    let mut N: T = point.clone();
    let mut n = scalar.clone();
    let mut Q: T = T::identity();

    while n != Scalar::zero() {
        if !n.is_even() {
            Q = &Q + &N;
        };

        N = N.double();
        n = n.half();
    }  
    Q
}

And It gives me errors on all of the Q’s and N’s saying that:

error[E0506]: cannot assign to `Q` because it is borrowed
  --> src/edwards.rs:46:13
   |
36 | pub fn double_and_add<'b, 'a, T>(point: &'a T, scalar: &'b Scalar) -> T 
   |                           -- lifetime `'a` defined here
...
46 |             Q = &Q + &N;
   |             ^   --
   |             |   |
   |             |   borrow of `Q` occurs here
   |             |   requires that `Q` is borrowed for `'a`
   |             assignment to borrowed `Q` occurs here

error[E0597]: `Q` does not live long enough
  --> src/edwards.rs:46:17
   |
36 | pub fn double_and_add<'b, 'a, T>(point: &'a T, scalar: &'b Scalar) -> T 
   |                           -- lifetime `'a` defined here
...
46 |             Q = &Q + &N;
   |                 ^^
   |                 |
   |                 borrowed value does not live long enough
   |                 requires that `Q` is borrowed for `'a`
...
53 | }
   | - `Q` dropped here while still borrowed

error[E0597]: `N` does not live long enough
  --> src/edwards.rs:46:22
   |
36 | pub fn double_and_add<'b, 'a, T>(point: &'a T, scalar: &'b Scalar) -> T 
   |                           -- lifetime `'a` defined here
...
46 |             Q = &Q + &N;
   |                      ^^
   |                      |
   |                      borrowed value does not live long enough
   |                      requires that `N` is borrowed for `'a`
...
53 | }
   | - `N` dropped here while still borrowed

error[E0506]: cannot assign to `N` because it is borrowed
  --> src/edwards.rs:49:9
   |
36 | pub fn double_and_add<'b, 'a, T>(point: &'a T, scalar: &'b Scalar) -> T 
   |                           -- lifetime `'a` defined here
...
46 |             Q = &Q + &N;
   |                      --
   |                      |
   |                      borrow of `N` occurs here
   |                      requires that `N` is borrowed for `'a`
...
49 |         N = N.double();
   |         ^ assignment to borrowed `N` occurs here

error[E0597]: `N` does not live long enough
  --> src/edwards.rs:49:13
   |
36 | pub fn double_and_add<'b, 'a, T>(point: &'a T, scalar: &'b Scalar) -> T 
   |                           -- lifetime `'a` defined here
...
49 |         N = N.double();
   |             ^---------
   |             |
   |             borrowed value does not live long enough
   |             argument requires that `N` is borrowed for `'a`
...
53 | }
   | - `N` dropped here while still borrowed

error[E0505]: cannot move out of `Q` because it is borrowed
  --> src/edwards.rs:52:5
   |
36 | pub fn double_and_add<'b, 'a, T>(point: &'a T, scalar: &'b Scalar) -> T 
   |                           -- lifetime `'a` defined here
...
46 |             Q = &Q + &N;
   |                 --
   |                 |
   |                 borrow of `Q` occurs here
   |                 requires that `Q` is borrowed for `'a`
...
52 |     Q
   |     ^ move out of `Q` occurs here

But since the variables are declared as mutable on the biggest scope of the function, I can not understand why I have the lifetime issues.
So for the add or doubling operations, I only get a reference to them, nothing else.

'a last for the whole function and by saying where &'a T: ... you’re only saying the traits are implemented if the reference last for 'a.

Use where for<'c> &'c T: ... to say they’re implemented for all possible lifetimes.

In general you should avoid reusing lifetimes where possible.

3 Likes

Ohh that was a good one.

Thank you a lot.