Search code examples
genericsrusthigher-kinded-types

Generic struct over a generic type without type parameter


Is it possible to do something like this in Rust?

trait Foo<T> {}

struct A;
struct B;

struct Bar<T: Foo> {
    a: T<A>,
    b: T<B>
}

I know I could just use two parameters for Bar, but I think there has to be a better way to do this.

I want to implement a Graph structure. As I can't just bind the nodes and edges to their parents lifetime, I want to have something like Rc. However, sometimes one may need a Graph with access from multiple threads. So I'd have to have both an implementation with Rc and Arc.

That's what Foo is good for: I implement Foo for both Rc and Arc (Foo would require Deref) and I use a parameter T bound to Foo. That's how I wanted to have one struct for single thread and multi thread usage.


Solution

  • You can use generic associated types (GATs) and the family pattern for that:

    trait Family {
        type Container<T>;
    }
    
    struct A;
    struct B;
    
    struct Bar<T: Family> {
        a: T::Container<A>,
        b: T::Container<B>,
    }
    

    Then you can define two families:

    struct BoxFamily;
    impl Family for BoxFamily {
        type Container<T> = Box<T>;
    }
    
    struct VecFamily;
    impl Family for VecFamily {
        type Container<T> = Vec<T>;
    }
    

    And use it:

    let boxes: Bar<BoxFamily> = Bar {
        a: Box::new(A),
        b: Box::new(B),
    };
    
    let vecs: Bar<VecFamily> = Bar {
        a: vec![A, A],
        b: vec![B],
    };
    

    (Playground)

    As you can see, it's slightly more involved than one would hope for: you can't just say Bar<Vec> for example, but have to go through the extra family type. But it works!

    For an older answer (before GATs existed) containing more general information about the topic, click here.