Implementing the Sum Trait


#1

Is there a way to implement the Sum trait for non-std types? I’ve been searching for an answer and have yet to come across anything on how it might be done or if it is possible. I will start with the type I would like to sum, how I’ve implemented it now, and then how I would like to implement it to start.

I have this Point the type I would like to sum.

pub struct Point {
    x: f64,
    y: f64,
}

And this is how I am currently summing it. The behaviour I would like is for the x and y from each Point to be added together.

pub fn sum_points(points: &[&Point]) -> Point {
    Point {
        x: points.iter().map(|point| point.x).sum(),
        y: points.iter().map(|point| point.y).sum(),
    }
}

I would like to be able to write it like this.

pub fn sum_points(points: &[&Point]) -> Point {
    points.iter().sum()
}

But as I said I have not been able to find a way to do this yet. I have read enough to have been able to implement a subtraction.

use std::ops::Sub;

impl Sub for &Point {
    type Output = Point;

    fn sub(self, other: &Point) -> Point {
        Point {
            x: self.x - other.x,
            y: self.y - other.y,
        }
    }
}

But when I have tried to do similar things (copying from the Rust source code) I have not quite been able to get it to work.

std::iter::Sum;

impl Sum for Point {
    fn sum(iter: Iterator<Item = Point>) -> Point {
        iter.fold(Point { x: 0.0, y: 0.0 }, |a, b| Point {
            x: a.x + b.x,
            y: a.y + b.y,
        })
    }
}

impl<'a> Sum<&'a Point> for Point {
    fn sum(iter: Iterator<Item = &'a Point>) -> Point {
        iter.fold(Point { x: 0.0, y: 0.0 }, |a, b| Point {
            x: a.x + b.x,
            y: a.y + b.y,
        })
    }
}

I am hoping someone can point me in the right direction to get this to work. Even if it is not currently possible it would be nice just to know why.

pub fn sum_points(points: &[&Point]) -> Point {
    points.iter().sum()
}

Thanks!


#2

The way you’ve written this is using a trait as a value type, which you can’t do directly since trait objects are unsized. Your method needs to be generic just as Sum has declared it:

fn sum<I>(iter: I) -> Self
where
    I: Iterator<Item = A>
{
    //...
}

#3

Thanks @cuviper, I’ll try that at some point today. From looking at what you wrote (which is similar to what is in the Rust docs) I’m still not sure what then the implementation would look like if you are still using generics. What then goes in //...?

Because without trying it this look quite right unless I am missing some kind of pattern matching magic?

fn sum<I>(iter: I) -> Self where I: Iterator<Item = A> {
    iter.fold(Point { x: 0.0, y: 0.0 }, |a, b| Point {
        x: a.x + b.x,
        y: a.y + b.y,
    })
}

#4

A is the generic type name used in the std::iter::Sum trait definition that represents the types you’re summing, but in your implementation of that trait you need to fill it in with a concrete type, e.g.:

impl<'a> Sum<&'a Self> for Point {
    fn sum<I>(iter: I) -> Self
    where
        I: Iterator<Item = &'a Self>,
    {
        iter.fold(Self { x: 0.0, y: 0.0 }, |a, b| Self {
            x: a.x + b.x,
            y: a.y + b.y,
        })
    }
}

You can replace all those Self instances with Point as well - Self is just a “shortcut” for the type you’re implementing the trait for (useful if, e.g., you rename the type, then don’t need to change these occurrences).


#5

Sorry, I copied the signature directly from Sum, but I should have filled in APoint and &'a Point respectively, or with Self is fine too. The generic part I was trying to highlight was the I in fn sum<I>.

The function body that you have looks fine. With I: Iterator, you can still fold it the same way.


#6

Thank you @vitalyd that did the trick. And no worries @cuviper it was a good start I just needed a little bit more help.

Even though I have got it going I’ll have to take a look at the Rust Book again. It has been a while since I have and I either missed, forgot, or they didn’t have the part on Item when I read it (quite likely I just forgot as I was brand new to Rust then).

In any case I really appreciate the help. Good work keeping the Rust community welcoming to ask questions like this. :+1:t4: