繁体   English   中英

实现具有 function 返回特征的特征

[英]Implement trait that has function which return traits

我正在尝试为一个结构实现一个特征,该结构又具有返回特征的函数。 我想要这个,因为我不想将 uer 绑定到特定的数据结构。 然而,试图应用编译器的修正建议,我越陷越深,无济于事。 这是我正在尝试做的一个最小示例:

trait WordsFilter {
    fn starting_with(&self, chr: char) -> dyn Iterator<Item = String>;
}

struct WordsContainer {
    words: Vec<String>,
}

impl WordsFilter for WordsContainer {
    fn starting_with(&self, chr: char) -> dyn Iterator<Item = String>
    {
        self.words.iter().filter(|word| word.starts_with("a"))
    }
}

fn main() {}

结果是:

error[E0277]: the size for values of type `(dyn Iterator<Item = String> + 'static)` cannot be known at compilation time
  --> .\traits.rs:10:40
   |
10 |     fn starting_with(&self, chr: char) -> dyn Iterator<Item = String>
   |                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `Sized` is not implemented for `(dyn Iterator<Item = String> + 'static)`
   = note: the return type of a function must have a statically known size

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.

我尝试逐步应用编译器的更正,但它们越来越多。

TL;DR 使用 generics 或相关类型。

您不能使用裸dyn Trait类型。 它们是Unsized的,这意味着编译器在编译时无法知道它们的大小。 因此,您只能在某些引用后面使用它们(类似于您不能使用str[T]而必须使用&str&[T]的方式)。 最简单的方法是使用Box<dyn Trait> ,就像PitaJ之前建议的那样。 这当然需要一个堆分配,但允许非常好的 API。

但是,在普通函数和方法中,您可以使用impl Trait技巧使编译器推断返回类型。 这是这样使用的

fn foo() -> impl Iterator<Item = ()> {
    todo!()
}

调用此 function 将返回一些实现给定特征的具体 object。 然而,这在 Trait 定义中目前是不可能的。 有积极的发展,希望将来我们能够使用它。 然而现在并非如此。 所以下面的代码将无法编译!

// XXX: This doesn't currently work!
trait Foo {
    fn foo() -> impl Iterator<Item = ()>;
}

幸运的是,有一个不需要装箱特征对象的简单解决方案。 您可以定义关联类型并要求每个实现者指定一个具体类型。 您还可以要求此类型必须实现Iterator特性。

trait WordsFilter {
    type Iter: Iterator<Item = String>;
    
    fn starting_with(&self, chr: char) -> Self::Iter;
}

这将使每个实现指定一种具体类型的返回迭代器。 如果您希望能够为单一类型返回多个不同的迭代器,您可以使用 generics。

trait WordsFilter<I>
where
    I: Iterator<Item = String>
{
    fn starting_with(&self, chr: char) -> I;
}

有几件事情需要解决才能让它发挥作用。

我将首先展示工作版本,然后逐步说明必须更改的内容和原因。

trait WordsFilter {
    fn starting_with(&self, chr: char) -> Box<dyn Iterator<Item = String> + '_>;
}

struct WordsContainer {
    words: Vec<String>,
}

impl WordsFilter for WordsContainer {
    fn starting_with(&self, chr: char) -> Box<dyn Iterator<Item = String> + '_>
    {
        Box::new(self.words.iter().filter(|word| word.starts_with("a")).cloned())
    }
}

fn main() {}
  1. 装箱迭代器。 Rust 不允许您返回裸dyn类型。 编译器错误告诉您将其装箱。 所以我做了。

  2. 过滤后克隆。 Vec<X>::iter()返回一个带有Item=&X的迭代器,所以在这个例子中我们得到Item=&String我们想要Item=String所以我们需要克隆。

  3. 加一生。 问题是如果没有生命周期检查我可以写这个......

     let words_container = WordsContainer{...}; let it = words_container.starting_with(); drop(words_container) it.next()

    但是it.next()正在逐步通过words_container内部的内部向量 - 它不再存在。

    在 trait 上添加'_ lifetime 表示迭代器仅在底层容器有效时才有效。 所以现在上面的代码会产生一个编译错误。

暂无
暂无

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

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