然后sum() 知道返回i32
这是关键的缺失点。虽然“输入”类型是已知的(它必须是实现 Iterator 的东西才能使 sum 可用),但“输出”类型非常灵活。
查看Iterator::sum:
fn sum<S>(self) -> S
where
S: Sum<Self::Item>,
它返回一个泛型类型S,它必须实现Sum。 S 不必须匹配 Self::Item。因此,编译器要求您指定什么
输入总和。
为什么这很有用?查看标准库中的这两个示例实现:
impl Sum<i8> for i8
impl<'a> Sum<&'a i8> for i8
没错!你可以总结一个u8的迭代器或者一个&u8的迭代器!如果我们没有这个,那么这段代码将无法工作:
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,我们可以通过 关联类型 来实现这一点,该类型将绑定 u8 -> u8 和 &'a u8 -> u8。
如果我们只有一个关联类型,那么目标总和类型将始终是固定的,我们将失去灵活性。详情请见When is it appropriate to use an associated type versus a generic type?。
例如,我们还可以为我们自己的类型实现Sum<u8>。在这里,我们总结了u8s,但增加了我们求和的类型的大小,因为总和可能会超过u8。此实现是标准库中现有实现的补充:
#[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);
}