Trait as a Generic Parameter

Consider the following illegal code:

pub trait Remote_Eval_T {
  trait Context_T;
  ...
  fn do_stuff(&self, context: &Context_T)
  ...
}

here we are saying: We can define a Remote_Eval_T. For every object that implements trait Remote_Eval_T, it has an associated Context_T, which we later use in one of the functions.

We can't write the above code, so instead we have to write:


pub trait Context_T {}

pub struct Context {
  inner: Rc<dyn Context_T>
}

pub trait Remote_Eval_T {
  type Context;
  ...
  fn do_stuff(&self, context: &Context)
  ...
}

This has the ugliness that every time we add something to trait Context_T, we have to add a 'forwarding' fn to struct Context.

====

Is there a better way to solve this problem ?

What's wrong with just using a bound on the associated type?

trait Context_T {}

pub trait Remote_Eval_T {
  type Context_T_Impl: Context_T;
  fn do_stuff(&self, context: &Self::Context_T_Impl) {}
}
1 Like
struct Bar {}
struct Foo {}

impl Remote_Eval_T for Bar {
  Context_T = Bar_Context_T here
}

impl Remote_Eval_T for Foo {
  Context_T = Foo_Context_T here
}

I.e. the Context_T is not a global constant, but depends on the struct that implements Remote_Eval_T

In particular, Bar_Context_T might have something like fn punch while Foo_Context_T has something like fn fire_rocket

That's exactly what the associated type means.

No problem - implementation of the trait can use specific properties of the associated type (as long as they're public, of course).

2 Likes

I'm starting to think you're correct; I need to rethink the design here.

Only guessing here, but the difficulty may come when you want to use dyn RemoteEval, as the associated types have to match (dyn RemoteEval<Context = SomeType>). One way around that is you end up with a dyn RemoteEval<Context = Box<dyn Context>> or the like.

This will probably come up in the language more once we get RPITIT [1], as the return types are effectively associated types [2]. See here for example.


  1. return-position impl Trait in Traits ↩︎

  2. and async functions utilize RPIT ↩︎

Thanks for trying to help. This question is botched, due to my fault, as I tangled a number of issues (still not sure how to frame the question). Suppose we have something like this:

crateA:
  trait Tank_T:
    fire_missile
    move
  trait Solider_T:
    punch
    move

crate_B:
  impl Remote_Eval_T for Blah { using Solider_T }
  impl Remote_Eval_T for Blah2 { using Tank_T }

crate_C:
  struct RifleMan {}; impl Solider_T for RifleMan;
  struct SwordMan {}; impl Solider_T for SwordMan;
  struct LightTank {}; impl Tank_T for LightTank;
  struct HeavyTank {}; impl Tank_T for HeavyTank;

Then suppose (for external reasons), we want crate B to only depend on crate A, but not depend on crate C.

So we now have the problem that Remote_Eval_T only has access to the traits Solider_T and Tank_T, but not the actual impls.

TLDR: There is this tangled mess I failed to capture in the original question.

In this example, is Blah and Blah2 the context, or is the RifleMan, ... etc?

There are generally two approaches I can see:

trait EvalForTarget {
  type Target;

  fn eval(&self, target: &Self::Target);
}

struct EvalTank<T>(PhantomData<T>);

impl<T: Tank> EvalForTarget for EvalTank<T> {
  type Target = T;
  fn eval(&self, target: T) ...
}

or

trait EvalAnyTank {
  fn eval_tank(&self, target: &dyn Tank); // or impl Tank / fn generic parameter if you don't need object safety.
}

Both only make sense if you have a higher level crate that can see both B and C, of course.