I found this question on the Rust users forum : Generics: Can I say "tuple where each element is FromSql". Basically, the questions was to know how do something like that :
trait Foo {}
struct A {}
impl Foo for A {}
struct B {}
impl Foo for B {}
fn main() {
let x = (A{}, A{}, B{}, A{});
bar(x);
}
fn bar<T: Foo>(tuple: (T...)) {
}
This code does not work, it's an idea of how it could look like.
So, how can we do that?
ToAny
trait that will be implemented for all our structures.use std::any::Any;
pub trait ToAny {
fn as_any(&self) -> &dyn Any;
}
trait Foo: ToAny {}
It requires implementing the
ToAny
trait to force each structure implementingFoo
to implementToAny
too.
struct A {
id: i32,
}
impl ToAny for A {
fn as_any(&self) -> &dyn Any {
self
}
}
impl Foo for A {}
struct B {
id: i32,
}
impl ToAny for B {
fn as_any(&self) -> &dyn Any {
self
}
}
impl Foo for B {}
The implementation of ToAny
is always the same, we could create a macro implementing it easily.
Vec
instead of a tuple to store our values:let boxeds: Vec<Box<dyn A>> = vec![
Box::new(A {id: 1}),
Box::new(B {id: 2}),
Box::new(A {id: 3}),
Box::new(B {id: 4}),
];
// Stores the values being `B`.
let mut bees: Vec<&B> = vec![];
for boxed in &boxeds {
// `Some(x)` if `boxed` contains a `B` value.
let found = match boxed.as_any().downcast_ref::<B>() {
Some(b) => b,
None => {}, // it is a `A` value.
};
bees.push(found);
}
assert_eq!(bees, vec![
&B {id: 2},
&B {id: 4}
]);
If we refer to the question, the following code:
fn bar<T: Foo>(tuple: (T...)) {
}
can be this valid code:
fn bar(values: Vec<Box<dyn Foo>>) {
}
I've written a gist file being tests for that. Be careful, I've changed the names in this post, Foo
is A
and there is only the B
structure.