【问题标题】:Why does type inference behave differently for `HashMap` and my own struct in the presence of a type parameter default?为什么在存在类型参数默认值的情况下,“HashMap”和我自己的结构的类型推断行为不同?
【发布时间】:2019-04-10 15:18:03
【问题描述】:

我有一个带有两个类型参数的结构,其中一个具有默认类型:

use std::marker::PhantomData;

struct Foo<T, F = ()>(PhantomData<(T, F)>);

impl<T, F> Foo<T, F> {
    fn new() -> Self { Self(PhantomData) }
    fn foo(&self, _: T) {}
}


let foo = Foo::new();
foo.foo(0u32);

上面的代码导致:

error[E0282]: type annotations needed
  --> src/main.rs:17:15
   |
17 |     let foo = Foo::new();
   |         ---   ^^^^^^^^ cannot infer type for `F`
   |         |
   |         consider giving `foo` a type

我不明白为什么这里没有使用默认类型。请注意,let foo: Foo&lt;u32&gt; = Foo::new(); 已经有效——因此无需指定参数F。但是为什么要指定T?所以我已经很困惑了。

但后来我记得所有这些都适用于HashMap!它被定义为struct HashMap&lt;K, V, S = RandomState&gt;。我从来不需要指定任何东西。例如,这有效:

use std::collections::HashMap;

let mut map = HashMap::new();
map.insert(0u32, 'x');

(Everything on Playground)

为什么FooHashMap 之间的默认类型/推理行为不同?哈希图是否使用了一些编译器魔法?

【问题讨论】:

  • 即使let foo: Foo&lt;_&gt; = Foo::new(); 也可以正常工作,因为可以推断出T。看起来它没有提供任何额外的信息,但实际上它确实提供了——显式省略类型构造函数的第二个参数告诉编译器使用默认值。没有任何类型注释的代码的行为类似于let foo: Foo&lt;_, _&gt; = Foo::new();,它失败并出现相同的错误。

标签: rust


【解决方案1】:

HashMap::new 是这样定义的:

impl<K: Hash + Eq, V> HashMap<K, V, RandomState> {
    pub fn new() -> HashMap<K, V, RandomState> {
        Default::default()
    }
}

RandomStateS 提供new。对于相同的行为,您的代码将如下所示:

impl<T> Foo<T, ()> {
    fn new() -> Self { Self(PhantomData) }
    fn foo(&self, _: T) {}
}

Playground

注意:Default 可用于自定义BuildHasher

impl<K, V, S> Default for HashMap<K, V, S>

【讨论】:

  • 所以简而言之:无法推断的F是impl的参数F,而不是struct的参数F。如果您将 impl 的参数重命名为其他名称,则会从错误消息中清楚地看到这一点。
  • 但这意味着,您不能为F = i32 调用newfoo,对吗?有可能实现吗?
  • @hellow 正确,如果new() 只为F = () 定义,则不能为其他类型F 调用它。如果您也想允许这样做,您需要添加另一个更灵活的方法,使用不同的名称,或者像 HashMap 那样使用 Default::default()
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-27
  • 1970-01-01
  • 2023-04-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多