How to provide type-only argument to a function?

rust

Solution

If you want to pass a type argument to a function, you can just do it. You don't have to leave it out to be inferred.

This is how it looks for your second example (playground):

fn pack_item<C: Container>(item: C::Item) -> ItemPacket {
    ItemPacket {  
        container_description: C::describe_container(),
        _payload: item.get_payload(),
    }
}

fn main() {
    let s = pack_item::<ImAContainerType>(ImAnItemType);
    println!("{}", s.container_description);
    let s = pack_item::<ImAnotherContainerType>(ImAnItemType);
    println!("{}", s.container_description);
}

Problem

In my short Rust experience I ran into this pattern several times, and I'm not sure if the way I solve it is actually adequate... Let's assume I have some trait that looks like this: ``` trait Container { type Item; fn describe_container() -> String; } ``` And some struct that implements this trait: ``` struct ImAContainerType; struct ImAnItemType; impl Container for ImAContainerType { type Item = ImAnItemType; fn describe_container() -> String { "some container that contains items".to_string() } } ``` This may be a container that has a knowledge about type of items it contains, like in this example, or, as another example, request which knows what type of response should be returned, etc. And now I find myself in a situation, when I need to implement a function that takes an item (associated type) and invokes a static function of the container (parent trait). This is the first naive attempt: ``` fn describe_item_container<C: Container>(item: C::Item) -> String { C::describe_container() } ``` This does not compile, because associated types are not injective, and `Item` can have several possible `Container`s, so this whole situation is ambiguous. I need to somehow provide the actual `Container` type, but without providing any container data. I may not have the container data itself at all when I invoke this function! In search for a solution, I find the documentation for std::marker::PhantomData. It says: PhantomData allows you to describe that a type acts as if it stores a value of type T, even though it does not. This has to be the Rust's replacement for Haskell's Proxy type, right? Let's try to use it: ``` 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); ``` Compiling... Error: ``` error[E0277]: the trait bound `std::marker::PhantomData<ImAContainerType>: Container` is not satisfied ``` I ask `#rust-beginners` and get a response: `PhantomData` is not meant to be used that way at all! Also I got an advice to simply make a backward associated type link from `Item` to `Container`. Something like this: ``` trait Item { type C: Container; } fn describe_item_container<I: Item>(item: I) -> String { I::C::describe_container() } ``` It should work, but makes things much more complicated (especially for cases when item can be placed in different container kinds)... After a lot more experimentation, I do the following change and everything compiles and works correctly: ``` let s = describe_item_container(PhantomData::<ImAContainerType>, ImAnItemType); println!("{}", s); ``` The change is `::<PhantomData<ImAContainerType>>` to `::<ImAContainerType>`. Playground example. It works, but now I'm completely confused. Is this the correct way to use `PhantomData`? Why does it work at all? Is there some other, better way to provide type-only argument to a function in Rust? EDIT: There is some oversimplification in my example, because in that particular case it would be easier to just invoke `ImAContainerType::describe_container()`. Here is some more complicated case, when the function actually does something with an `Item`, and still requires container type information.

Original source