英文:
Rust generics: list where each element is from the same trait?
问题
我在Rust用户论坛上找到了这个问题:Generics: Can I say "tuple where each element is FromSql"。基本上,问题是想知道如何做类似这样的事情:
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...)) {
}
这段代码不起作用,只是一个可能的样子。
那么,我们该如何做呢?
英文:
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?
答案1
得分: 0
- 创建一个名为
ToAny
的特性,将为我们的所有结构体实现它。
use std::any::Any;
pub trait ToAny {
fn as_any(&self) -> &dyn Any;
}
- 让我们创建我们的特性
trait Foo: ToAny {}
这需要实现
ToAny
特性,以强制每个实现Foo
的结构体也实现ToAny
。
- 让我们创建我们的结构体:
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 {}
ToAny
的实现始终相同,我们可以轻松创建一个宏来实现它。
- 然后,我们可以创建一个
Vec
来存储我们的值,而不是使用元组:
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}),
];
// 存储是`B`的值。
let mut bees: Vec<&B> = vec![];
for boxed in &boxeds {
// 如果`boxed`包含`B`值,则为`Some(x)`。
let found = match boxed.as_any().downcast_ref::<B>() {
Some(b) => b,
None => {}, // 这是`A`值。
};
bees.push(found);
}
assert_eq!(bees, vec![
&B {id: 2},
&B {id: 4}
]);
如果我们参考问题,以下代码:
fn bar<T: Foo>(tuple: (T...)) {
}
可以成为有效的代码:
fn bar(values: Vec<Box<dyn Foo>) {
}
我在这个帖子中已更改了名称,Foo
是 A
,并且只有 B
结构体。
英文:
- The first step is to create a
ToAny
trait that will be implemented for all our structures.
use std::any::Any;
pub trait ToAny {
fn as_any(&self) -> &dyn Any;
}
- Let's create our trait
trait Foo: ToAny {}
> It requires implementing the ToAny
trait to force each structure implementing Foo
to implement ToAny
too.
- Let's create our structures:
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.
- And then, we can create a
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.
答案2
得分: 0
使用宏来为元组实现 `Foo`:
```rust
trait Foo {
fn do_it(&self);
}
struct A {}
impl Foo for A {
fn do_it(&self) {
print!("A");
}
}
struct B {}
impl Foo for B {
fn do_it(&self) {
print!("B");
}
}
fn main() {
let x = (A{}, A{}, B{}, A{});
bar(x);
}
fn bar<T: Foo>(tuple: T) {
tuple.do_it();
}
macro_rules! impl_Foo_for_tuple {
( $first:ident $($rest:ident)* ) => {
impl<$first: Foo, $( $rest : Foo, )* > Foo for ( $first, $($rest,)* ) {
fn do_it(&self) {
#[allow(non_snake_case)]
let ( $first, $($rest,)* ) = self;
$first.do_it();
$( $rest.do_it(); )*
}
}
impl_Foo_for_tuple!( $($rest)* );
};
() => {};
}
impl_Foo_for_tuple!(A B C D E F G H I J K L M);
<details>
<summary>英文:</summary>
Use a macro to implement `Foo` for tuples:
```rust
trait Foo {
fn do_it(&self);
}
struct A {}
impl Foo for A {
fn do_it(&self) {
print!("A");
}
}
struct B {}
impl Foo for B {
fn do_it(&self) {
print!("B");
}
}
fn main() {
let x = (A{}, A{}, B{}, A{});
bar(x);
}
fn bar<T: Foo>(tuple: T) {
tuple.do_it();
}
macro_rules! impl_Foo_for_tuple {
( $first:ident $($rest:ident)* ) => {
impl< $first: Foo, $( $rest : Foo, )* > Foo for ( $first, $($rest,)* ) {
fn do_it(&self) {
#[allow(non_snake_case)]
let ( $first, $($rest,)* ) = self;
$first.do_it();
$( $rest.do_it(); )*
}
}
impl_Foo_for_tuple!( $($rest)* );
};
() => {};
}
impl_Foo_for_tuple!(A B C D E F G H I J K L M);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论