【问题标题】:Cannot infer type for type parameter `S` when using HashSet::from_iter使用 HashSet::from_iter 时无法推断类型参数“S”的类型
【发布时间】:2020-07-17 07:45:07
【问题描述】:

我正在尝试解决一个涉及比较两组的在线挑战。我按照this 的回答将我的Vec<i32> 输出转换为HashSet

use std::collections::HashSet;
use std::iter::FromIterator;

struct Solution {}

impl Solution {
    pub fn solve(nums: Vec<i32>, k: i32) -> Vec<i32> {
        // todo, return dummy for now
        return vec![1, 2];
    }
}

fn main() {
    assert_eq!(
        HashSet::from_iter(Solution::solve(vec![1, 2, 3], 2)),
        HashSet::from_iter(vec![1i32, 2i32])
    )
}

由于我还不明白的原因,编译失败:

error[E0282]: type annotations needed
  --> src/main.rs:15:9
   |
15 |         HashSet::from_iter(Solution::solve(vec![1, 2, 3], 2)),
   |         ^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `S` declared on the struct `HashSet`

它适用于HashSet::from_iter(vec![1i32, 2i32])

我尝试添加像HashSet::from_iter::&lt;Vec&lt;i32&gt;&gt; 这样的类型注释,但无济于事。我也阅读了source implementation,但仍然无法弄清楚编译器抱怨的原因。

我可以通过显式声明或使用 for 循环和 inserts 构造 HashSet 来解决它,但我想了解这里发生了什么。

我正在使用 Rust 1.43.1。

【问题讨论】:

标签: rust


【解决方案1】:

我相信 answer from Kitsu 是不正确的,因为它表明 Rust 无法推断 T 的类型,因为这里可能是为类型 P 实现的迭代器,它收集了不同的类型 T(更多关于这个最后)。

真正发生了什么

其实HashSet&lt;T, S&gt;中的类型S的类型推断与T的类型无关。问题是您的程序中没有任何信息可以让编译器推断S 的类型。

添加类型参数足以解决歧义是正确的:

HashSet::<i32>::from_iter(vec![1, 2, 3]);

这与您指定的实际类型无关。事实上,这也有效:

HashSet::<_>::from_iter(vec![1, 2, 3]);

原因是标准库中HashSet的定义包含了S的默认类型:

pub struct HashSet<T, S = RandomState> {
    base: base::HashSet<T, S>,
}

通过编写HashSet::&lt;_&gt;::from_iter(vec![1, 2, 3]);,您是在告诉编译器它应该使用S 的默认类型,即RandomState

多重实现呢?

Kitsu's answer 指出S 的类型推断失败,因为FromIterator 可能有多个实现。这是不正确的,但有多个实现可能会导致 T 的类型推断失败。

考虑这个例子:

fn iterator_demo() {
    use std::collections::HashSet;
    use std::hash::{BuildHasher, Hash};
    use std::iter::FromIterator;

    struct Empty;

    impl<T, S> FromIterator<Empty> for HashSet<T, S>
    where
        T: Eq + Hash,
        S: BuildHasher + Default,
    {
        fn from_iter<I>(iter: I) -> HashSet<T, S>
        where
            I: IntoIterator<Item = Empty>,
        {
            iter.into_iter().for_each(drop);
            HashSet::default()
        }
    }

    let x = HashSet::<_>::from_iter(vec![Empty, Empty]);
}

这会导致类型推断失败;请注意,这是无法推断出T,而不是S

error[E0282]: type annotations needed for `HashSet<T>`
  --> src/lib.rs:22:13
   |
22 |     let x = HashSet::<_>::from_iter(vec![Empty, Empty]);
   |         -   ^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
   |         |
   |         consider giving `x` the explicit type `HashSet<T>`, with the type parameters specified

指定类型解决了这种歧义:

let x = HashSet::<String>::from_iter(vec![Empty, Empty]);

感谢 Rust Discord 上的 matt1992 帮助我了解这里真正发生的事情。

【讨论】:

  • 自从我问这个问题以来已经写了更多的 Rust,当归结为原因时,这个答案似乎确实是准确的。事实上,在没有一个以上的 from_iter impl 的情况下,rust 会自动解析 T。 @Evan Rust 不能推断使用默认 S = RandomState 是有原因的吗?
  • 我认为类型推断要成功,它必须是明确的。如果您指定HashMap::&lt;_&gt;::from_iter,则S 只能是RandomState。如果你不这样做,那么 HashMap 可以有任何 &lt;T,S&gt; 类型参数。编译器可能会猜测您的意思是&lt;i32, RandomState&gt;,但这只是猜测,编译器不会进行猜测。
  • 我不明白为什么编译器不能使用默认的S。如果我想要一个非默认的S,我需要明确指定它。编译器不必“猜测”任何东西。它适用于::&lt;_&gt;,但并非没有,这令人困惑。
【解决方案2】:

让我们看看你一直在使用的FromIterator::from_iter 声明:

fn from_iter<T>(iter: T) -> Self
where
    T: IntoIterator<Item = A>,

在指定HashSet 之后,编译器可以推断出Self 对于某些SHashSet&lt;S&gt;T 对于您的特定情况,可以推断为 Vec&lt;i32&gt;: IntoIterator&lt;Item = i32&gt;(对于在您明确指定整数类型后解析的第二行)。

但是,S 仍然没有被推断出来,因为一般来说,您可能有一个从IntoIterator&lt;Item = u8&gt; 收集HashSet&lt;u64&gt; 的实现。因此编译器无法理解收集类型的项目是什么。那么如果你交换assert_eq错误源的表达式变化:

assert_eq!(
    HashSet::from_iter(vec![1i32, 2i32]),
/*
   |
15 |     HashSet::from_iter(vec![1i32, 2i32]),
   |     ^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `S` declared on the struct `HashSet`
*/
    HashSet::from_iter(Solution::solve(vec![1, 2, 3], 2)),
)

解决方案相当简单:您需要指定 HashSet 的项目类型:

assert_eq!(
    HashSet::<i32>::from_iter(Solution::solve(vec![1, 2, 3], 2)),
    HashSet::from_iter(vec![1i32, 2])
)

【讨论】:

  • 答案是有道理的,一旦第一个被显式类型注释,编译器是否能够自动从 vec 宏中为第二个推断 S?还是那里有什么特殊行为?
  • 是的,类似于if Vec::&lt;usize&gt;::new() == vec![] {} 语句。第二个Vec 的类型不需要指定,因为它是从第一个推导出来的。
  • >但是,S 仍然没有被推断出来,因为一般来说,您可能有一个从IntoIterator&lt;Item = u8&gt; 收集HashSet&lt;u64&gt; 的实现。有趣的。你有什么类型的例子吗?
猜你喜欢
  • 2019-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-06
  • 1970-01-01
  • 2022-01-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多