为什么 Rust 不能推断出 Iterator::sum 的结果类型?

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

此代码有效:

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

如果我从 a 中删除 i32 类型,则会出现此错误:

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

我原以为 Rust 会把 (1i32..10) 变成一个 i32 迭代器然后 sum() 知道 return 一个 i32。我错过了什么?

and then sum() knows to return an i32

这是关键的缺失点。虽然 "input" 类型已经为人所知(它必须实现 Iterator 才能使 sum 可用),但 "output" 类型非常灵活。

查看 Iterator::sum:

fn sum<S>(self) -> S
where
    S: Sum<Self::Item>,

它 returns 一个必须实现 Sum 的泛型类型 SS 不必 匹配 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);
}

,我们可以通过 关联类型 来完成此操作,该类型将绑定 u8 -> u8&'a u8 -> u8

如果我们只有一个关联类型,那么目标总和类型将始终是固定的,我们就会失去灵活性。有关详细信息,请参阅

例如,我们也可以为自己的类型实现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);
}

定义方式sum,return取值不限;不止一种类型可以实现特征Sum<i32>。这是一个示例,其中使用了 a 的不同类型,两者都编译:

#[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

由于两种结果类型都是可能的,因此无法推断出类型,必须通过涡轮鱼 (sum::<X>()) 或作为表达式的结果 (let x: X = ...sum();) 明确指定类型。