- A Guided Tour of Streams in Rust(【译】Rust 中的流式接口指南)
- 原文链接:https://www.qovery.com/blog/a-guided-tour-of-streams-in-rust
- reddit 链接:https://www.reddit.com/r/rust/comments/uotnwc/a_guided_tour_of_streams_in_rust/
- 原文作者:Romain Gérard
- 译文来自:https://github.com/suhanyujie/article-transfer-rs/
- ps:水平有限,如有不当之处,欢迎指正。
- 标签:Rust,stream
导读
我们在调研如何给 Qovery 基础架构编写 GRPC 或 Websocket 服务器时,我了解到了很多资源。但是,尽管许多指南提供了对 future 的深入见解,但它们严重缺乏关于 Stream API 在 Rust 中如何工作的文档。更重要的是,如何正确使用它。遗憾的是,我们不能对流视而不见。一旦你更喜欢或关注我们钟爱的 REST api 的简单请求/响应协议,而不太了解流、异步生成器等概念,就容易出现一些问题。对于 Rust 来说尤其如此。当你决定在 GRPC 中使用 tonic 或在 Websocket 中使用 tokio tungstenite 时,这些库中唯一可用的接口都是基于流的。这就是为什么本文关注于在 Rust 的上下文中引入流。
正文
流(stream)是什么?
从异步世界来看,流是一个迭代器。如果习惯于同步世界,你观察一个迭代器,它看起来就像这样:
Iterator<MyItem>
它表示可以检索的 0…N 的 MyItem 序列,那迭代器呢。很有可能,你已经亲眼见过了。它们早就出现在 Java 1.2 版中,你也可以在 C++ 标准库中找到它们,以及在 [Python] 中(https://wiki.python.org/moin/Iterator) 也能看到。迭代器通常在你想要迭代一个集合(比如一个列表,一个向量,一个树等等)时用到。它是一种常见的抽象,可以与集合实现解耦,并表示可以以线性方式检索的一系列项。在 Rust 中,迭代器只有 2 个特殊项:
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
- 一个关联类型
Item
,表示迭代器将返回的对象类型 - 一个
next()
方法,用于返回 Option 包裹的Item
类型对象
**为什么是 Option?**当返回类型有可能是 None 时,使用 Option 是有用的,因为它可以告诉你迭代器没有任何元素剩余,现在耗尽了。如果你以 Java API 为例,它有两个方法 — 一个叫做 next()
,这和 Rust 中一样,不过 Rust 直接返回一个 Item;另一个方法叫做 hasNext()
。作为开发人员,在调用 next()
之前调用 hasNext()
取决于你。但如果是忘记这样做,就构成了一个逻辑错误,可能导致程序崩溃。通过合并 hasNext()
和 next()
,并返回一个 Option
, Rust 防止了这种错误的产生,并为开发人员提供了一个更安全的 API 接口。
流:异步迭代器
现在我们已经讨论了迭代器是什么,让我们回到流(streams)。正如我们所看到的,流是迭代器的异步版本。我们看看它的定义:
pub trait Stream {
type Item;
fn poll_next(
self: Pin<&mut Self>,
cx: &mut Context
) -> Poll<Option<Self::Item>>;
}
等等, poll/pin 是什么?让我们先把它们放在一边,然后修改定义以更好地理解:
pub trait Stream {
type Item;
fn next(self: &mut Self ) -> impl Future<Output = Option<Self::Item>>;
// Warning, the trait does not really return a Future, it is more correct to say that Stream trait is also a Future
// but for the sake of the explanation, we are going to say that next returns a future
}
由于特殊的 async 关键字,在 Rust 中返回 Future 的方式可以不用。所以,如果我们再次改变定义,流的定义就相当于: