This may be a bit of XY or technically out of topic, but I think it ought to be quite useful, since it will be yielding actionable advice after the nice (type-)theoretical considerations already mentioned in this post.
The situation
So, for starters, I think you have simplified your example a bit too much: if there is a Target
type, then I suspect you actual use case has method using it:
trait Rule<'a> {
type Target : Action + 'a;
/// Let's assume you have this kind of method:
fn get_action (self: &'a Self)
-> Self::Target
;
}
And for dyn Action
to be useful, let's also add a method to it:
trait Action {
fn act (self: &'_ Self)
;
}
Your objective is to, from a bunch of stuff implementing Rule
, to get a collection of these rules to potentially end up making their actions .act()
:
/* pseudo-code! */
let mut rules = vec![];
rules.push(&some_rule);
rules.push(&some_other_rule);
for rule in &rules {
rule.get_action().act();
}
At that point, indeed, you'll need to use dyn
traits to "collect" different types within the same Vec
, and the associated type prevents you from doing so.
The solution
β¦ is to use a helper trait, which will perform the intermediary coercion for you:
trait DynRule<'a> {
fn dyn_get_action (self: &'a Self)
-> Box<dyn 'a + Action>
;
}
impl<'a, T> DynRule<'a> for T
where
T : Rule<'a>,
{
fn dyn_get_action (self: &'a Self)
-> Box<dyn 'a + Action>
{
Box::new(self.get_action())
}
}
And, as you can see, DynRule<'a>
no longer has an associated type, so you are free to collect &'_ dyn DynRule<'_>
:
/* implementors */
struct FireAction;
impl<'a> Action for &'a FireAction {}
struct FireRule;
impl<'a> Rule<'a> for FireRule {
type Target = &'a FireAction;
/// Let's assume you have this kind of method:
fn get_action (self: &'a Self)
-> Self::Target
{
&FireAction {}
}
}
struct SkipAction;
impl<'a> Action for &'a SkipAction {}
struct SkipRule;
impl<'a> Rule<'a> for SkipRule {
type Target = &'a SkipAction;
/// Let's assume you have this kind of method:
fn get_action (self: &'a Self)
-> Self::Target
{
&SkipAction {}
}
}
/* ββββββββββββ */
fn main ()
{
let rules: Vec<&dyn DynRule<'_>> = vec![&FireRule {}, &SkipRule {}];
for rule in rules {
rule.dyn_get_action().act();
}
}
Note that given that you are dealing with &'a β¦
-shaped return types, that is, if all the returned actions are returned by reference, then the Box
can be skipped by yielding &dyn Action
s instead, provided the implementor of Action
is not the returned reference but its referee. This also allows getting rid of the lifetime parameter. Playground