铁 rust 仿制药:列出每个元素来自同一个特性的位置

bprjcwpo  于 2023-02-19  发布在  其他
关注(0)|答案(2)|浏览(96)

我在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...)) {

}

这个代码不工作,这是一个想法,它可能看起来像.
那么,我们如何才能做到这一点?

ffx8fchx

ffx8fchx1#

  • 第一步是创建一个ToAny trait,它将为我们所有的结构实现。
use std::any::Any;

pub trait ToAny {
    fn as_any(&self) -> &dyn Any;
}
  • 让我们创建自己的特性
trait Foo: ToAny {}

它需要实现ToAny trait来强制每个实现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}),
];

// 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}
]);

如果我们参考这个问题,下面的代码:

fn bar<T: Foo>(tuple: (T...)) {

}

可以是此有效代码:

fn bar(values: Vec<Box<dyn Foo>>) {

}

我已经写了a gist file来测试它。小心,我在这篇文章中更改了名称,FooA,只有B结构。

krcsximq

krcsximq2#

使用宏实现元组的Foo

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);

相关问题