返回特征的特征:在某些情况下有效,在其他情况下则无效



我需要实现一个返回futures::StreamExt特性的特性。

总的来说,这听起来很容易,对此有几个答案,例如这里的这个。

我在StreamExt上尝试过,但由于某种原因,这不起作用。这里是我的样本代码:


// [dependencies]
// futures = "0.3.6"
// async-std = { version = "1.6.5", features = ["attributes", "unstable"] } // interval function is unstable atm.
use std::time::Duration;
use futures::StreamExt;
trait StreamProvidingTrait {
fn returnastream(self: &Self) -> Box<dyn StreamExt<Item=i32>>;
}
struct StreamProvider {}
impl StreamProvidingTrait for StreamProvider {
fn returnastream(self: &Self) -> Box<dyn StreamExt<Item=i32>> {
return Box::new(async_std::stream::interval(Duration::from_millis(1000)).map(|_| 1));
}
}
#[async_std::main]
async fn main() {
let mut counter = 0;
let object = StreamProvider {};
// creates a stream
let worx = object.returnastream();
// mappes the stream into something....
let mut mapped_stream = worx.map(|_| {
counter = counter + 1;
counter
});
// subscribing to the items
while let item = mapped_stream.next().await {
match item {
Some(value) => println!("{}", value),
_ => {}
}
}
}

这里的错误:

Compiling traittest v0.1.0 (/Users/andre/repos/traittest)
warning: the trait `futures_util::stream::stream::StreamExt` cannot be made into an object
--> /Users/andre/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-util-0.3.6/src/stream/stream/mod.rs:1326:8
|
1326 |     fn poll_next_unpin(&mut self, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>
|        ^^^^^^^^^^^^^^^ the trait cannot be made into an object because method `poll_next_unpin` references the `Self` type in its `where` clause
|
= note: `#[warn(where_clauses_object_safety)]` on by default
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #51443 <https://github.com/rust-lang/rust/issues/51443>
error[E0038]: the trait `futures_util::stream::stream::StreamExt` cannot be made into an object
--> src/main.rs:6:36
|
6    |   fn returnastream(self: &Self) -> Box<dyn StreamExt<Item=i32>>;
|                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `futures_util::stream::stream::StreamExt` cannot be made into an object
|
::: /Users/andre/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-util-0.3.6/src/stream/stream/mod.rs:226:8
|
226  |     fn next(&mut self) -> Next<'_, Self>
|        ---- the trait cannot be made into an object because method `next` references the `Self` type in its return type
...
976  |     fn by_ref(&mut self) -> &mut Self {
|        ------ the trait cannot be made into an object because method `by_ref` references the `Self` type in its return type
...
1381 |     fn select_next_some(&mut self) -> SelectNextSome<'_, Self>
|        ---------------- the trait cannot be made into an object because method `select_next_some` references the `Self` type in its return type
error[E0038]: the trait `futures_util::stream::stream::StreamExt` cannot be made into an object
--> src/main.rs:12:36
|
12   |   fn returnastream(self: &Self) -> Box<dyn StreamExt<Item=i32>> {
|                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `futures_util::stream::stream::StreamExt` cannot be made into an object
|
::: /Users/andre/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-util-0.3.6/src/stream/stream/mod.rs:226:8
|
226  |     fn next(&mut self) -> Next<'_, Self>
|        ---- the trait cannot be made into an object because method `next` references the `Self` type in its return type
...
976  |     fn by_ref(&mut self) -> &mut Self {
|        ------ the trait cannot be made into an object because method `by_ref` references the `Self` type in its return type
...
1381 |     fn select_next_some(&mut self) -> SelectNextSome<'_, Self>
|        ---------------- the trait cannot be made into an object because method `select_next_some` references the `Self` type in its return type
error: aborting due to 2 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0038`.
error: could not compile `traittest`.
To learn more, run the command again with --verbose.
Process finished with exit code 101

当我将StreamExt特性与自己的特性进行交换时,此代码可以完美地工作。


trait SomeTrait {
fn returnatrait(self: &Self) -> Box<dyn SomeOtherTrait>;
}
trait SomeOtherTrait {
fn sayHelloWorld(&self);
}
struct DummyStruct {}
impl SomeOtherTrait for DummyStruct {
fn sayHelloWorld(&self) {
println!("hello world");
}
}
struct Implementation {}
impl SomeTrait for Implementation {
fn returnatrait(self: &Self) -> Box<dyn SomeOtherTrait> {
return Box::new(DummyStruct{});
}
}
fn main() {
let implementation = Implementation{};
let worx = implementation.returnatrait();
worx.sayHelloWorld();
}

这里怎么了?很明显,有些事情我不明白。请帮我理解这一点!

返回特征的函数可以使用impl Trait语法返回实现该特征的不透明类型。返回trait的trait方法目前不支持此功能,需要将trait作为trait对象返回——动态调度的引用或智能指针,如BoxRc。然而,并不是所有的特性都是对象安全的,坏消息是StreamExt是不安全的,原因是编译器指出的,例如在方法返回类型和where子句中引用Self

然而,好消息是这不是问题:StreamExt是一个扩展特性,它为实现Stream的所有类型提供了一个一揽子实现。因此,您不需要麻烦返回dyn StreamExt特征对象,您可以返回dyn Stream特征对象,并且您仍然可以通过使用use StreamExt请求StreamExt方法来访问它们。换言之,您可以在特性的返回类型中将Box<dyn StreamExt>替换为Box<dyn Stream>

您可能会遇到的另一个问题是,Box<dyn Stream>不适用于需要移动流的方法,其中包括StreamExt提供的许多方法。这些将需要固定流,这可以通过返回Pin<Box<dyn Stream>>而不是Box<dyn Stream>来修复。StreamExt上甚至有一种boxed()方法,它在一次操作中对流进行封装;使用它的代码看起来像这样(操场(:

use futures::stream::{Stream, StreamExt};
use std::pin::Pin;
trait StreamProvidingTrait {
fn returnastream(&self) -> Pin<Box<dyn Stream<Item = i32>>>;
}
struct StreamProvider {}
impl StreamProvidingTrait for StreamProvider {
fn returnastream(&self) -> Pin<Box<dyn Stream<Item = i32>>> {
return tokio::stream::once(0).boxed();
}
}
fn main() {
let provider = StreamProvider {};
let stream = provider.returnastream();
let _fut = stream.into_future();
}

相关内容

最新更新