Why can't Rust infer the resulting type Iterator :: sum?

This code works:

fn main() { let a: i32 = (1i32..10).sum(); let b = a.pow(2); } 

If I remove the i32 type from a , I will get this error:

 rustc 1.13.0 (2c6933acc 2016-11-07) error: the type of this value must be known in this context --> <anon>:3:13 | 5 | let b = a.pow(2); | ^^^^^^^^ 

Run example

I would expect Rust to turn (1i32..10) into an i32 iterator, and then sum() knows how to return i32 . What am I missing?

+8
types type-inference rust
source share
2 answers

The method sum defined, the return value is open; more than one type can implement the Sum<i32> . Here is an example where different types are used for a , both of which are compiled:

 #[derive(Clone, Copy)] struct Summer { s: isize, } impl Summer { fn pow(&self, p: isize) { println!("pow({})", p); } } impl std::iter::Sum<i32> for Summer { fn sum<I>(iter: I) -> Self where I: Iterator<Item = i32>, { let mut result = 0isize; for v in iter { result += v as isize; } Summer { s: result } } } fn main() { let a1: i32 = (1i32..10).sum(); let a2: Summer = (1i32..10).sum(); let b1 = a1.pow(2); let b2 = a2.pow(2); } 

Playground

Since both types of results are possible, the type cannot be inferred and must be explicitly determined either using turbodiz ( sum::<X>() ) or as a result of the expression ( let x: X = ...sum(); ).

+7
source share

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); } 
+5
source share

All Articles