[英]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() {}
装箱迭代器。 Rust 不允许您返回裸dyn
类型。 编译器错误告诉您将其装箱。 所以我做了。
过滤后克隆。 Vec<X>::iter()
返回一个带有Item=&X
的迭代器,所以在这个例子中我们得到Item=&String
我们想要Item=String
所以我们需要克隆。
加一生。 问题是如果没有生命周期检查我可以写这个......
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.