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

时间:2016-12-07 12:10:41

标签: types rust type-inference

此代码有效:

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

如果我从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 the example

我原以为Rust会将(1i32..10)转换为i32迭代器,然后sum()知道返回i32。我错过了什么?

2 个答案:

答案 0 :(得分:11)

定义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);
}

Playground

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

答案 1 :(得分:7)

  

然后sum()知道返回i32

这是关键缺失点。虽然&#34;输入&#34;类型已经知道(它必须是实现Iterator才能使sum可用),&#34;输出&#34;类型非常灵活。

结帐Iterator::sum

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

它返回一个必须实现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);
}

As bluss points out,我们可以通过使用关联类型来实现此目标,该类型会绑定u8 -> u8&'a u8 -> u8

如果我们只有一个关联的类型,那么目标总和类型将始终是固定的,我们将失去灵活性。有关详细信息,请参阅When is it appropriate to use an associated type versus a generic type?

作为示例,我们还可以为我们自己的类型实现Sum<u8>。在这里,我们总结了u8 s,但增加了我们求和的类型的大小,因为它的总和可能会超过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);
}