Interdependent generics

I have a struct Tree<D, M> where D is the data stored in the tree and M is a metadata type.

I also have a Builder struct that needs to store an impl Tree<D, M>. This is the declaration right now:

pub struct Builder<D, M, T> 
where
  T: Tree<D, M>
{
  tree: T,
  _phantom: PhantomData<(D, M)>,
  // Fields hidden for example
}

However, this means that when creating a Builder, you need to specify each type twice:
let mut builder = Builder::<Kind, usize, BasicTree<Kind, usize>::new()

I'd prefer to be able to do something like:
let mut builder = Builder::<BasicTree<Kind, usize>::new()

Is this currently possible in Rust?

It's not clear to me why you couldn't do this. Do you have a concrete example that you want to work but doesn't?

If I had to guess, you're putting bounds on your builder that don't need to be there, and you thought the way around the "unconstrained parameters" was to add parameters to Builder. If possible, try just having a

struct Builder<T>(Option<T>); // or whatever fields

and only putting bounds on implementations which actually need the bounds.

If it's not possible, you need to supply more context.

Sorry, should've given compiler errors.

If I have a

pub struct Builder<T> {
    pub tree: T,
}

and then add an implementation:

impl<D, M, T> Builder<T>
where
  T: Tree<D, M>,
{}

I get E0207, saying that the type parameter D is not constrained by the impl trait, self type, or predicates (and same for M). If I try to remove T like this:

impl<D, M> Builder<Tree<D, M>> { }

I get E0782 saying that Tree<D, M> isn't a concrete type and I'd have to use the dyn keyword. If I instead try to remove D and M like this:

impl<T> Builder<T> 
where
    T: Tree<D, M>
{ }

I get E0412 saying that D and M are undefined

The only way I've gotten this to compile correctly is to move the trait bounds to the struct declaration like I had in the original post

pub struct Builder<D, M, T> 
where
  T: Tree<D, M>
{ 
//...
}

This compiles, but then I have to specify both D and M twice as in Builder::<Kind, usize, BasicTree<Kind, usize>>

edit: In the last code snippet, Builder was called Tree. My bad

Using the same name for a struct and a trait is going to make some of the errors more confusing, so I suggest not doing that.

With distinct naming,

pub struct Builder<T> {
    pub tree: T,
}

impl<D, M, T: TreeTrait<D, M>> Builder<Tree<D, M, T>> {
    pub fn new() -> Self {
        todo!()
    }
}

works fine.

(You may have unneeded parameters on the Tree<D, M, T> too, hard to say so far.)

It seems to me that you are confusing traits with types; you are using Tree both as if it were a trait and a type.

If it really is a type, then parameterize the builder as Builder<D, M>, and use Tree<D, M> whenever you need to refer to the tree type.

Sorry, there was a typo in the last example I provided. Tree<D, M> is a trait, while Builder<T> and BasicTree<D, M> are both structs.

This:

impl<D, M, T: Tree<D, M>> Builder<T> {

}

gives the same compiler error as before, where it says that the type parameter D is not constrained by the impl trait, self type, or predicates

Remove the bounds off the struct (or if you don't think you can, show why).

Alright ngl, I definitely butchered this whole post. I'm kinda tired right now. Anyway, I figured it out so in case anyone else has this problem:

I have a group of tree-type structs, E,g. BasicTree<D, M>, InterningTree<D, M>, etc. My idea to give them all shared behavior involved a Tree<D, M> trait. The solution was to remove the generics off the Tree trait and instead use associated types, e.g.

impl<D, M> Tree for BasicTree<D, M> {
  type Data = D;
  type Meta = M;

  fn foo(d: Self::Data, m: Self::Meta) {
    // ...
  }
}

My understanding is that, by having generics on both the struct and trait, I was telling Rust that BasicTree<D, M> could hypothetically implement Tree<D, M> even when both Ds and both Ms are not equal, I.E., BasicTree<Kind, usize> could implement Tree<u32, PhantomData> if it wanted to. Instead it makes more sense to only have trait bounds on either the structs or the trait.

This was very much an XY problem post, sorry for the confusion and I appreciate all the help :pray:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.