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

Tim*_*mmm 16 types type-inference rust

此代码有效:

fn main() {
    let a: i32 = (1i32..10).sum();
    let b = a.pow(2);
}
Run Code Online (Sandbox Code Playgroud)

如果我i32从中删除类型a,那么我收到此错误:

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 Code Online (Sandbox Code Playgroud)

运行示例

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

Chr*_*son 15

sum定义方式,返回值是开放式的; 多种类型可以实现特征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);
}
Run Code Online (Sandbox Code Playgroud)

操场

由于两种结果类型都是可能的,因此无法推断类型,必须通过turbofish(sum::<X>())或表达式(let x: X = ...sum();)的结果显式指定.


She*_*ter 8

然后sum()知道返回一个i32

这是关键的缺失点.虽然"输入"类型已经知道(它必须是Iterator为了sum甚至可用而实现的),但"输出"类型非常灵活.

退房Iterator::sum:

fn sum<S>(self) -> S
where
    S: Sum<Self::Item>,
Run Code Online (Sandbox Code Playgroud)

它返回一个S必须实现的泛型类型Sum.S 不必匹配Self::Item.因此,编译器要求您指定要汇总的类型.

为什么这有用?从标准库中查看这两个示例实现:

impl Sum<i8> for i8
impl<'a> Sum<&'a i8> for i8
Run Code Online (Sandbox Code Playgroud)

那就对了!你可以总结一个迭代器u8 迭代器&u8!如果我们没有这个,那么这段代码不起作用:

fn main() {
    let a: i32 = (0..5).sum();
    let b: i32 = [0, 1, 2, 3, 4].iter().sum();
    assert_eq!(a, b);
}
Run Code Online (Sandbox Code Playgroud)

作为bluss指出,我们可以通过具有实现这一目标相关联的类型,其将配合u8 -> u8&'a u8 -> u8.

如果我们只有一个关联的类型,那么目标总和类型将始终是固定的,我们将失去灵活性.请参阅何时使用关联类型与泛型类型?更多细节.

作为一个例子,我们也可以Sum<u8>为我们自己的类型实现.在这里,我们总结了u8s,但增加了我们总结的类型的大小,因为它可能总和会超过a 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);
}
Run Code Online (Sandbox Code Playgroud)

  • 我认为缺少一个合乎逻辑的步骤;仅仅因为多个类型想要求和为相同的值,并不意味着必须有另一个类型参数。它可以通过关联类型“&amp;'a i8 -&gt; i8”和“i8 -&gt; i8”等来解决。 (2认同)