rustphantom-types

Using a trait as phantom type


In Rust, I want to use a phantom type to properly type a simple id:

struct Id<T> {
    val: u32,
    _type: PhantomData<T>,
}

In a first draft version, I used concrete structs as T, all was well. Then in a more elaborate version using differents data sources, those structs became traits. Let's say:

trait MyArticle {
    fn get_id() -> Id<MyArticle>;
}

But using traits as phantom types brings problems:

My current solution is to duplicate name types between an empty struct and a trait:

struct MyArticle_ {};

trait MyArticle {
    fn get_id() -> Id<MyArticle_>;
}

This is awkward but I could not find better.


Solution

  • The problem with your sample lies in understanding what the trait is. And in fact it is not a type (that's why compiler asks for T: ?Sized), but a requirement for a type. Thus the solution is fairly simple: come up with a "real" type. You got it right with a struct declaration, it can be one option. But usually it's much more convenient to use associative type for that:

    trait MyArticle {
        type T;
    
        fn get_id() -> Id<Self::T>
        where
            Self::T: MyArticle;
    }
    
    // so given impls
    struct X;
    
    impl MyArticle for X {
        type T = u32;
        fn get_id() -> Id<u32> {
            todo!()
        }
    }
    
    impl MyArticle for u32 {
        type T = u32;
        fn get_id() -> Id<u32> {
            todo!()
        }
    }
    

    So finally, you're able to call X::get_id(), or a fully-qualified version: <X as MyArticle>::get_id()

    Also, you may read there why fn get_id() -> Id<Box<dyn MyArticle>> doesn't work.