I am trying to implement a generic structure with a bunch of fields, where each of the field types should know about the exact type of the whole structure. It\'s a sort of strat
If the following impl
is characteristic for Strategy
, then it might be parameterized on the wrong thing. (I'm going to ignore the associated type for this answer, because the example doesn't use it.)
impl> Strategy for ExampleStrat {
fn run(&self, e: &E) {
let _ = e.data();
// uses ExampleData here
}
}
You could instead parameterize Strategy
over D
-- breaking the impl
dependency cycle -- and parameterize only the run
method over E
.
pub trait Strategy {
fn run(&self, &impl HasData);
}
impl Strategy for ExampleStrat {
fn run(&self, e: &impl HasData) {
let _ = e.data();
// uses ExampleData here
}
}
fn run
is another way to define run
that is the same for this purpose. Here is a full example.
A potential drawback of this approach is that run
can't be called through a Strategy
trait object, because it has to be monomorphized for any type that implements HasData
. But the HasData
trait doesn't seem to do much in this impl
: the only thing it can do is return an internal reference, and once you have it, there's no point in using it again. Maybe run
could just take a &D
reference?
pub trait Strategy {
fn run(&self, &D);
}
impl Strategy for ExampleStrat {
fn run(&self, _: &ExampleData) {
// uses ExampleData here
}
}
To be sure, now you have to call self.s.run(self.data())
in do_it
, but this doesn't cost you in flexibility over the original version, in which, had it worked¹, you could only call Strategy
with an argument of type &E
.
In fact, the whole HasData
trait seems unnecessary to me: it's always implemented by the same type whose implementation calls it, so aside from the minor convenience of passing self
instead of self.data
, it doesn't elevate the level of abstraction inside the do_it
method. So it seems to me effectively the same thing to delete HasData entirely and let Example
know how to call Strategy::run
with the right reference; it has to, anyway. (However, it's possible I merely lack imagination.)
Any of these solutions ought to handle adding an associated type to Strategy
, but without knowing how it will be used, it's hard to say for sure.
¹It could be made to work in some future version of the compiler, with sufficiently smart type checking.