This code works:
fn main() { let a: i32 = (1i32..10).sum(); let b = a.pow(2); }
If I remove the i32
type from a
, then I 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 the example
I would have expected that Rust turns (1i32..10)
into an i32
iterator and then sum()
knows to return an i32
. What am I missing?
The way sum
is defined, the return value is open-ended; more than one type can implement the trait Sum<i32>
. Here's an example where different types for a
are used, both of which compile:
#[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 result types are possible, the type cannot be inferred and must be explicitly specified, either by a turbofish (sum::<X>()
) or as the result of the expression (let x: X = ...sum();
).
and then
sum()
knows to return ani32
This is the key missing point. While the "input" type is already known (it has to be something that implements Iterator
in order for sum
to even be available), the "output" type is very flexible.
Check out Iterator::sum
:
fn sum<S>(self) -> S where S: Sum<Self::Item>,
It returns a generic type S
which has to implement Sum
. S
does not have to match Self::Item
. Therefore, the compiler requires you to specify what type to sum into.
Why is this useful? Check out these two sample implementations from the standard library:
impl Sum<i8> for i8 impl<'a> Sum<&'a i8> for i8
That's right! You can sum up an iterator of u8
or an iterator of &u8
! If we didn't have this, then this code wouldn't 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 points out, we could accomplish this by having an associated type which would tie u8 -> u8
and &'a u8 -> u8
.
If we only had an associated type though, then the target sum type would always be fixed, and we'd lose flexibility. See When is it appropriate to use an associated type versus a generic type? for more details.
As an example, we can also implement Sum<u8>
for our own types. Here, we sum up u8
s, but increase the size of the type we are summing, as it's likely the sum would exceed a u8
. This implementation is in addition to the 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); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With