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);
| ^^^^^^^^
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 traitSum<i32>
. Here's an example where different types fora
are used, both of which compile: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();
).This is the key missing point. While the "input" type is already known (it has to be something that implements
Iterator
in order forsum
to even be available), the "output" type is very flexible.Check out
Iterator::sum
:It returns a generic type
S
which has to implementSum
.S
does not have to matchSelf::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:
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: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 upu8
s, but increase the size of the type we are summing, as it's likely the sum would exceed au8
. This implementation is in addition to the existing implementations from the standard library: