简体   繁体   English

泛型函数可以用特征参数化吗?

[英]Can a generic function be parameterized with a trait?

Is it possible to pass traits as parameters to generic functions like this?是否可以将特征作为参数传递给这样的泛型函数?

trait Fnord {
    fn do_it(&self) -> i32 { 42 }
}

impl Fnord for i32 {}

fn iter_as<'a, T>(objs: &'a [i32]) -> impl Iterator<Item = & 'a dyn T>
{
    objs.iter().map(|o| o as &dyn T)
}

fn main() {
    let objs: Vec<i32> = vec![1, 2, 3];

    // Calls would look like this
    for s in iter_as::<Fnord>(&objs) {
        println!("{}", s.do_it());
    }
}

This produce me these errors:这给我带来了这些错误:

error[E0404]: expected trait, found type parameter `T`
 --> src/lib.rs:7:69
  |
7 | fn iter_as<'a, T>(objs: &'a [i32]) -> impl Iterator<Item = & 'a dyn T>
  |                                                                     ^ not a trait

error[E0404]: expected trait, found type parameter `T`
 --> src/lib.rs:9:35
  |
9 |     objs.iter().map(|o| o as &dyn T)
  |                                   ^ not a trait

warning: trait objects without an explicit `dyn` are deprecated
  --> src/lib.rs:16:24
   |
16 |     for s in iter_as::<Fnord>(&objs) {
   |                        ^^^^^ help: use `dyn`: `dyn Fnord`
   |
   = note: `#[warn(bare_trait_objects)]` on by default

That is, can iter_as accept a trait as a generic parameter so that it can return an iterable of that trait?也就是说, iter_as是否可以接受一个特征作为通用参数,以便它可以返回该特征的可迭代对象? I've searched quite a bit for an answer, but at this point I feel like I may be asking the wrong question.我已经搜索了很多答案,但此时我觉得我可能问错了问题。

The background is like this.背景是这样的。 I've got a struct with several vectors of different concrete types, all of which implement the same traits.我有一个结构体,其中包含多个不同具体类型的向量,所有这些向量都实现了相同的特征。 I'd like the struct's impl to have a function that can return an iterable over all of the stored objects as any of their common traits.我希望结构体的 impl 有一个函数,该函数可以返回所有存储对象的可迭代对象作为它们的任何共同特征。 The iter_as above is a simplified version of that (notional) function.上面的iter_as是该(名义上的)函数的简化版本。 Perhaps I'm simply approaching this in an awkward way for rust (ie maybe I'm thinking too much like a C++ programmer), so an alternative, idiomatic approach would be great, too.也许我只是以一种笨拙的方式来处理 Rust(即,也许我想得太像 C++ 程序员),所以另一种惯用的方法也很棒。

T must be a concrete type and not a trait. T必须是具体类型而不是特征。 The closer to what you're looking for that I can think to is the following:我认为更接近您正在寻找的内容如下:

trait Fnord {
    fn do_it(&self) -> i32;
}

impl Fnord for i32 {
    fn do_it(&self) -> i32 {
        *self
    }
}

impl<'a> From<&'a i32> for &'a dyn Fnord {
    fn from(i: &'a i32) -> Self {
        i as _
    }
}

fn iter_as<'a, T, TObj>(objs: &'a [T]) -> impl Iterator<Item = TObj> + 'a
where
    TObj: 'a,
    TObj: From<&'a T>,
{
    objs.iter().map(|o| o.into())
}

fn main() {
    let objs: Vec<i32> = vec![1, 2, 3];

    for s in iter_as::<i32, &dyn Fnord>(&objs) {
        println!("{}", s.do_it()); // 1 2 3
    }
}

I'm not sure that you've chosen the idiomatic Rust way to do that: since you know the concrete types that are in your object, you can write it as follow:我不确定您是否选择了惯用的 Rust 方式来做到这一点:既然您知道对象中的具体类型,您可以将其编写如下:

trait Fnord {
    fn do_it(&self) -> i32;
}

impl Fnord for i32 {
    fn do_it(&self) -> i32 {
        *self
    }
}

impl<'a> From<&'a i32> for &'a dyn Fnord {
    fn from(i: &'a i32) -> Self {
        i as _
    }
}

struct YourObject {
    v1: Vec<i32>,
    v2: Vec<i32>,
}

impl YourObject {
    fn iter_as<'a, T>(&'a self) -> impl Iterator<Item = T> + 'a
    where
        T: From<&'a i32>, // Add the other bounds you need
    {
        self.v1
            .iter()
            .map(|o| o.into())
            .chain(self.v2.iter().map(|o| o.into()))
    }
}

fn main() {
    let obj = YourObject {
        v1: vec![1, 2],
        v2: vec![3],
    };

    for s in obj.iter_as::<&dyn Fnord>() {
        println!("{}", s.do_it()); // 1 2 3
    }
}

The From implementation boilerplate can be reduced thanks to a macro:由于宏,可以减少From实现样板:

macro_rules! impl_from_for_dyn_trait {
    ( $concrete:ty, $trait:path ) => {
        impl<'a> From<&'a $concrete> for &'a dyn $trait {
            fn from(c: &'a $concrete) -> Self {
                c as _
            }
        }
    }
}

impl_from_for_dyn_trait!(i32, Fnord);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM