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

Posted

技术标签:

【中文标题】为啥 Rust 不能推断出 Iterator::sum 的结果类型?【英文标题】:Why can't Rust infer the resulting type of Iterator::sum?为什么 Rust 不能推断出 Iterator::sum 的结果类型? 【发布时间】:2017-04-22 09:03:42 【问题描述】:

此代码有效:

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() 知道返回 i32。我错过了什么?

【问题讨论】:

相关问题:***.com/q/40243061/1233251 【参考方案1】:

sum的方式定义,返回值是开放式的;不止一种类型可以实现特征Sum&lt;i32&gt;。这是一个使用不同类型的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::&lt;X&gt;()) 或作为表达式的结果 (let x: X = ...sum();) 显式指定类型。

【讨论】:

【参考方案2】:

然后sum() 知道返回i32

这是关键的缺失点。虽然“输入”类型是已知的(它必须是实现 Iterator 的东西才能使 sum 可用),但“输出”类型非常灵活。

查看Iterator::sum:

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

它返回一个泛型类型S,它必须实现SumS 必须匹配 Self::Item。因此,编译器要求您指定什么 输入总和。

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

impl Sum<i8> for i8
impl<'a> Sum<&'a i8> for i8

没错!你可以总结一个u8的迭代器或者一个&amp;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 -&gt; u8&amp;'a u8 -&gt; u8

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

例如,我们还可以为我们自己的类型实现Sum&lt;u8&gt;。在这里,我们总结了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);

【讨论】:

我认为缺少一个合乎逻辑的步骤;仅仅因为多种类型想要对同一事物求和,并不意味着必须有另一个类型参数。它可以通过关联类型&amp;'a i8 -&gt; i8i8 -&gt; i8 等来解决。 谢谢。我的意思是,这不是你的问题,它在图书馆里,但由于这个答案是通过一些步骤来合理化它,我想指出它。无论如何,合理化存在的 impl 可能是通往幸福的道路;-)

以上是关于为啥 Rust 不能推断出 Iterator::sum 的结果类型?的主要内容,如果未能解决你的问题,请参考以下文章

为啥浏览器可以推断出某些省略的 HTML 元素,但不能推断出形成有效标记所需的所有省略的元素?

为啥 Crystal 不能在初始化程序中推断出这种类型?

在 Rust 中的 iter 上使用 map 时,“无法推断 `_` 的类型”

为啥不能快速推断闭包类型

为啥方差注释会导致 Scala 无法推断出这种子类型关系?

为啥 TypeScript 在使用 concat 减少数组时会推断出“从不”类型?