Rust泛型:列表中的每个元素都来自相同的特性?

huangapple go评论63阅读模式
英文:

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&lt;T: Foo&gt;(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>) {

}

我在这个帖子中已更改了名称,FooA,并且只有 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(&amp;self) -&gt; &amp;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(&amp;self) -&gt; &amp;dyn Any {
        self
    }
}

impl Foo for A {}

struct B {
    id: i32,
}

impl ToAny for B {
    fn as_any(&amp;self) -&gt; &amp;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&lt;Box&lt;dyn A&gt;&gt; = 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&lt;&amp;B&gt; = vec![];

for boxed in &amp;boxeds {
    // `Some(x)` if `boxed` contains a `B` value.
    let found = match boxed.as_any().downcast_ref::&lt;B&gt;() {
        Some(b) =&gt; b,
        None =&gt; {}, // it is a `A` value.
    };

    bees.push(found);
}

assert_eq!(bees, vec![
    &amp;B {id: 2},
    &amp;B {id: 4}
]);

If we refer to the question, the following code:

fn bar&lt;T: Foo&gt;(tuple: (T...)) {

}

can be this valid code:

fn bar(values: Vec&lt;Box&lt;dyn Foo&gt;&gt;) {

}

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(&amp;self);
}

struct A {}
impl Foo for A {
    fn do_it(&amp;self) {
        print!(&quot;A&quot;);
    }
}

struct B {}
impl Foo for B {
    fn do_it(&amp;self) {
        print!(&quot;B&quot;);
    }
}

fn main() {
    let x = (A{}, A{}, B{}, A{});
    bar(x);
}

fn bar&lt;T: Foo&gt;(tuple: T) {
    tuple.do_it();
}

macro_rules! impl_Foo_for_tuple {
    ( $first:ident $($rest:ident)* ) =&gt; {
        impl&lt; $first: Foo, $( $rest : Foo, )* &gt; Foo for ( $first, $($rest,)* ) {
            fn do_it(&amp;self) {
                #[allow(non_snake_case)]
                let ( $first, $($rest,)* ) = self;
                $first.do_it();
                $( $rest.do_it(); )*
            }
        }
        impl_Foo_for_tuple!( $($rest)* );
    };
    () =&gt; {};
}
impl_Foo_for_tuple!(A B C D E F G H I J K L M);

huangapple
  • 本文由 发表于 2023年2月18日 21:36:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/75493701.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定