特质返回特质:在某些情况下可行,在其他情况下则不行。

5

我需要实现一个trait,该trait返回futures::StreamExt trait。

总体上这听起来很容易,有几个答案可以解决这个问题,例如这里

我尝试使用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();
}

这里出了什么问题?显然有些地方我不理解,请帮助我理解!


如果你在trait中添加一个像fn get_self(&self) -> &Self这样的函数会发生什么?这似乎在错误消息中显得突兀。 - Niklas Mohrin
1个回答

6
一种返回 trait 的函数可以使用 impl Trait 语法来返回实现该 trait 的不透明类型。目前,返回 trait 的 trait 方法 不支持 此功能,并且需要将 trait 返回为 trait 对象 —— 动态分派引用或智能指针,例如 BoxRc。然而,并非所有 trait 都是 对象安全。令人沮丧的是,StreamExt 就是其中之一,原因是编译器指出了如何在方法返回类型和 where 子句中引用 Self
然而,好消息是这不是问题:StreamExt是一个扩展特性,为所有实现Stream的类型提供了通用实现。因此,您不需要返回dyn StreamExt trait对象,可以返回dyn Stream对象,通过使用use StreamExt请求它们,仍然可以访问StreamExt方法。换句话说,在您的trait的返回类型中,只需将Box替换为Box即可。

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

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();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接