如何为函数提供仅类型参数?

时间:2016-10-26 02:54:01

标签: rust

在我的简短Rust经历中,我多次遇到这种模式,我不确定我解决它的方式是否足够......

让我们假设我有一些看起来像这样的特征:

trait Container {
    type Item;
    fn describe_container() -> String;
}

一些实现这个特性的结构:

struct ImAContainerType;
struct ImAnItemType;
impl Container for ImAContainerType {
    type Item = ImAnItemType;
    fn describe_container() -> String { "some container that contains items".to_string() }
}

这可能是一个容器,它知道它包含的项目类型,如本例所示,或者,作为另一个例子,知道应该返回什么类型的响应的请求等。

现在我发现自己处于某种情况,当我需要实现一个带有项(相关类型)的函数并调用容器的静态函数(父特征)时。这是第一次天真的尝试:

fn describe_item_container<C: Container>(item: C::Item) -> String {
    C::describe_container()
}

这不能编译,因为关联类型不是单射的,Item可能有几个Container s,所以整个情况都不明确。我需要以某种方式提供实际的Container类型,但不提供任何容器数据。当我调用这个函数时,我可能根本没有容器数据本身!

在寻找解决方案时,我找到了std::marker::PhantomData的文档。它说:

  

PhantomData允许您描述一个类型就像存储类型T的值一样,即使它没有。

这必须是Rust替换Haskell's Proxy type,对吧?让我们尝试使用它:

fn describe_item_container<C: Container>(container: PhantomData<C>, item: C::Item) -> String {
    C::describe_container()
}

let s = describe_item_container(PhantomData::<PhantomData<ImAContainerType>>, ImAnItemType);
println!("{}", s);

编译......错误:

error[E0277]: the trait bound `std::marker::PhantomData<ImAContainerType>: Container` is not satisfied

我问#rust-beginners并得到回复:PhantomData根本不应该以这种方式使用!另外,我得到一个建议,只需创建从ItemContainer的向后关联类型链接。像这样:

trait Item {
    type C: Container;
}
fn describe_item_container<I: Item>(item: I) -> String {
    I::C::describe_container()
}

它应该可以工作,但会使事情变得更复杂(特别是对于可以将物品放置在不同容器类型中的情况)......

经过更多的实验,我做了以下更改,所有内容都编译并正常运行:

let s = describe_item_container(PhantomData::<ImAContainerType>, ImAnItemType);
println!("{}", s);

更改为::<PhantomData<ImAContainerType>>::<ImAContainerType>

Playground example.

它有效,但现在我完全糊涂了。这是使用PhantomData的正确方法吗?为什么它可以工作?是否有其他更好的方法为Rust中的函数提供仅类型参数?

编辑:我的示例中有一些过度简化,因为在特定情况下,调用ImAContainerType::describe_container()会更容易。这是一些more complicated case,当函数实际上使用Item执行某些操作时,仍然需要容器类型信息。

1 个答案:

答案 0 :(得分:7)

如果要将类型参数传递给函数,则可以执行此操作。您不必将其遗漏以进行推断。

这是它查找第二个示例(playground)的方式:

@HostListener('window:resize', ['$event'])
onResize(event) {

}