Reputation: 1472
A trait like this prevents &dyn DoAction
because of the generic function:
trait DoAction {
fn action<T: T1 + T2>(&self, s: &T) {
s.action_t1();
s.action_t2();
}
}
Is there a way to write a function where the Vec
contains different concrete types, but they all implement the trait DoAction
?
fn run_all(runners: Vec<&impl DoAction>) {}
The main issue I want to solve is being able to loop over these different concrete types - but I cannot use Vec<&dyn T>
trait objects as decribed in How do I create a heterogeneous collection of objects? because of the generic trait function.
For example:
struct SA {
sa: u32,
}
struct SB {
sb: u32,
}
trait T1 {
fn action_t1(&self) -> bool {
true
}
}
trait T2 {
fn action_t2(&self) -> bool {
true
}
}
impl T1 for SA {}
impl T1 for SB {}
impl T2 for SA {}
impl T2 for SB {}
impl T1 for &SA {}
impl T1 for &SB {}
impl T2 for &SA {}
impl T2 for &SB {}
trait DoAction {
fn action<T: T1 + T2>(&self, s: &T) {
s.action_t1();
s.action_t2();
}
}
struct Runner1 {}
impl DoAction for Runner1 {}
struct Runner2 {}
impl DoAction for Runner2 {}
fn run_all(runners: Vec<&impl DoAction>, s: (impl T1 + T2)) {
for r in runners {
r.action(&s);
}
}
fn main() {
let a = SA { sa: 123 };
let r1 = Runner1 {};
let r2 = Runner2 {};
let ls = vec![&r1, &r2];
run_all(ls, &a);
}
Upvotes: 1
Views: 98
Reputation: 30061
Since such a function would only work for some concrete type, you can work around this by making a trait that "concretizes" the method and implement it for all types implementing DoAction
:
trait DoConcreteAction<T> {
fn concrete_action(&self, s: &T);
}
impl<T, U> DoConcreteAction<U> for T
where
T: DoAction,
U: T1 + T2,
{
fn concrete_action(&self, s: &U) {
self.action(s)
}
}
fn run_all<T>(runners: Vec<&dyn DoConcreteAction<T>>, s: &T) {
for r in runners {
r.concrete_action(&s);
}
}
fn main() {
let a = SA { sa: 123 };
let r1 = Runner1 {};
let r2 = Runner2 {};
let ls: Vec<&dyn DoConcreteAction<SA>> = vec![&r1, &r2];
run_all(ls, &a);
}
Upvotes: 4
Reputation: 13538
You can create a super trait that is automatically implemented for any type that implements T1
and T2
:
trait T1AndT2: T1 + T2 {}
impl<T: T1 + T2> T1AndT2 for T {}
And then change DoAction
to accept a trait object (T1AndT2
) instead of generics:
trait DoAction {
fn action(&self, s: &dyn T1AndT2) {
s.action_t1();
s.action_t2();
}
}
Now that DoAction
does not use generics, you can create a vector of DoAction
objects:
let ls: Vec::<&dyn DoAction> = vec![&r1, &r2];
And use it in your function:
run_all(ls);
Upvotes: 0