实现 returns 通用的特征函数

Implementing a trait function that returns generic

在下面的代码中,名为 LimitCollection 的特征有一个函数,return是一个实现 Iterator 特征的类型。

struct Limit {}

impl Limit {
    fn is_violated(&self) -> bool {
        todo!()
    }
}

trait LimitCollection {
    fn get_violated_limits<'a, I>(&'a self) -> I
    where
        I: Iterator<Item = &'a Limit>;
}

现在我想做一些实现特征的东西,但编译器抱怨我 return 的类型与通用 return 类型不匹配:“预期类型参数 I, 找到结构 Filter"

struct ConcreteLimitCollection {
    limits: Vec<Limit>,
}

impl LimitCollection for ConcreteLimitCollection {
    fn get_violated_limits<'a, I>(&'a self) -> I
    where
        I: Iterator<Item = &'a Limit>,
    {
        self.limits.iter().filter(Limit::is_violated)
    }
}

有没有什么方法可以使这个构造工作,最好不求助于动态分配?

通常你会使用关联类型,在这种情况下我们会涉及一些生命周期(我们需要 GAT,通用关联类型)。可悲的是,它所需要的还没有稳定下来:

#![feature(generic_associated_types)]

struct Limit {}

impl Limit {
    fn is_violated(&self) -> bool {
        todo!()
    }
}

trait LimitCollection {
    type Output<'a>: Iterator<Item = &'a Limit> where Self: 'a;
    fn get_violated_limits<'a>(&'a self) -> Self::Output<'a>;
}

struct ConcreteLimitCollection {
    limits: Vec<Limit>,
}

impl LimitCollection for ConcreteLimitCollection {
    type Output<'a> = Box<dyn Iterator<Item = &'a Limit> + 'a> where Self: 'a;
    
    fn get_violated_limits<'a>(&'a self) -> Self::Output<'a> {
        Box::new(self.limits.iter().filter(|l| l.is_violated()))
    }
}

Playground

免责声明,即使您不想使用动态分配,我也使用了 Box<dyn Iterator<Item = &'a Limit> + 'a>,但在这种情况下,您只需要完成整个 Filter<...> 类型。为了简单起见,只是使用了 Boxed 迭代器。

@SvenMarnach 适合这些类型 :

impl LimitCollection for ConcreteLimitCollection {
    type Output<'a> = std::iter::Filter<std::slice::Iter<'a, Limit>, fn(&&Limit) -> bool>;
    
    fn get_violated_limits<'a>(&'a self) -> Self::Output<'a> {
        self.limits.iter().filter(|l| l.is_violated())
    }
}

Playground

Is there any way to make this construct work, preferably without resorting to dynamic allocation?

泛型意味着调用者决定类型是什么,因为I(类型)是一个输入参数所以调用者应该能够传入只要满足约束,他们想要的就可以。

但这里不是这样的,你让来电者别无选择。

给调用者别无选择,但给trait implementer一个选择的方法是关联类型。

可悲的是,正如 Netwave 指出的那样,这里需要通用关联类型(因为 Iterator'a 生命周期内是通用的),并且 GAT 不稳定。

有两种方法可以参数化特征本身,并为引用而不是类型实现特征。 有各种可能性的示例和讨论。