Multiple Levels of Traits through Structs

I am attempting to fully utilize Traits (or Abstraction?) in which all Structs do not use types, but instead use Traits for variables. In doing so, I am running into the "issue" that a Struct needs to "know" about a Trait that it doesn't see on its' own "level".

Example, in which there are 3 Structs: A, B, C. A is at the bottom of the layer. B has a variable of type ATrait. C has a variable of type BTrait. But, C has to know of A and I don't want that. Is there a way around that?

I don't want to have to do: let c: C<B<A>>... to create a new C. I would like C to only know about B, let c: C<B>..., or something like that.

(My apologies on the bad terminology)

You can do let c: C<_>. And in fact, you don't need the type at all, since new_c returns a concrete type.

2 Likes

That works (updated example in which I added in a Struct D with function calls all the way down to A and a Testing/Mocking example).

A bit disappointed I can't get away with let d: D<C> = ..., but, no matter how many layers down I have traits, I can just do: let d: D<C<_>> = ... or let d: D<_> = ... (and that is weird).

And testing/mocking is simple enough as well.

However, the different "new" functions I will have to implement, that will have to have the different Struct Types denoted within the signatures is going to be fun: fn new_something() -> Something<A<B<C<D<E<....>>>>, but I guess I understand that; I have to declare some concrete type somewhere.

Thank you @drewtato.

What I would like to do, is in main, make a call like:

let d: D<SomeStructC> = some_fn();

In which SomeStructC is a Stuct that implements CTrait and some_fn() returns a D all without having to know of B or A (or any other Structs/Traits).

It seems that you are confused about generics. What you are asking is simply not possible. When you use generic parameters, it's the caller's responsibility to provide the generic argument.

1 Like

That makes sense. Thank you @firebits.io.

Type parameters defaults are incomplete/limited, but might be useful for you.[1]

pub struct A {}
pub struct B<ATRAIT = A> { .. }
pub struct C<BTRAIT = B> { .. } // B here means B<A>
pub struct D<CTRAIT = C> { .. } // C here means C<B<A>>

pub fn new_d() -> D { .. } // D here means D<C<B<A>>>

some_fn would have to know how to construct SomeStructC, e.g.

fn some_fn<SomeStructC: Default + CTrait>() -> D<SomeStructC> {
    D { c_trait: <_>::default() }
}

Though at that point, given what code you have so far, you might as well just

#[derive(Default)]
pub struct D<CTRAIT = C> { .. }

let d_foo = D::<Foo>::default();
// Required for both this and the previous code block:
// `Foo` implements `Default` too

Here's a modification of your playground, perhaps it will be useful.

That said, I don't know exactly what your XY is, but this is seeming pretty non-idiomatic to me. So far you're doing everything by value, but if one extends the "traits not fields!" idea just a bit, you'll end up with all fields being manipulated via getters and setters only. That doesn't play well with Rust's borrowing design.[2] It's rare to use getters/setters in the same module/crate where the fields are visible even if not public.


  1. Sort of long but if you read it all, it explains why you needed D<C<_>> without defaulted parameters in ascription position. ↩︎

  2. just accessing fields suddenly requires borrowing the entirety of self, leading to problems like these everywhere ↩︎

4 Likes

That is interesting, I'll see how I can put it in my existing code. Thank you @quinedot

Thank you for your responses @drewtato, @firebits.io, and @quinedot.

I have updated my example to do a mixture of Defaults and Generic Traits, in which each Struct declares a Default type and states that the Default type must Implement a Trait.

In this way, each Struct is able to create its' own new function to create its local variable without having to know anything about the other Structs' variables; no D<C<B<A>>>.

Final 2 questions:

  1. Is this "proper" code? As in, are there any drawbacks to this I need to be made aware of? Something along the lines of: "This is discouraged because..."
  2. Did I use terminology correctly? "I created a Struct with a Default that must Implement a Trait". Just so I can re-use this knowledge with others correctly.

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.