and then sum() knows how to return i32
This is a key missing point. Although the type of input is already known (it must be something that Iterator implements in order for sum even be available), the type of output is very flexible.
Check out Iterator::sum :
fn sum<S>(self) -> S where S: Sum<Self::Item>,
It returns the generic type S that sum should implement. S does not match Self::Item . Therefore, the compiler requires you to specify what type to sum.
Why is this useful? Check out these two implementation examples from the standard library:
impl Sum<i8> for i8 impl<'a> Sum<&'a i8> for i8
It is right! You can summarize the u8 iterator or the &u8 iterator! If we didn’t have this, then this code would not work:
fn main() { let a: i32 = (0..5).sum(); let b: i32 = [0, 1, 2, 3, 4].iter().sum(); assert_eq!(a, b); }
As bluss emphasizes , we could accomplish this by associating the type associated with u8 -> u8 and &'a u8 -> u8 .
If we had only a related type, then the target type of the sum would always be fixed, and we would lose flexibility. See When is it more suitable for you to use a linked type with a common type? .
As an example, we can also implement Sum<u8> for our own types. Here we summarize u8 s, but increase the size of the summed type, since, probably, the sum will exceed a u8 . This implementation is in addition to existing implementations from the standard library:
#[derive(Debug, Copy, Clone)] struct Points(i32); impl std::iter::Sum<u8> for Points { fn sum<I>(iter: I) -> Points where I: Iterator<Item = u8>, { let mut pts = Points(0); for v in iter { pts.0 += v as i32; } pts } } fn main() { let total: Points = (0u8..42u8).sum(); println!("{:?}", total); }