feat: added cleanscript
This commit is contained in:
125
ch17/ch17-04-streams.html
Normal file
125
ch17/ch17-04-streams.html
Normal file
@@ -0,0 +1,125 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Streams: Futures in Sequence</title>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Old headings. Do not remove or links may break. -->
|
||||
<p><a id="streams"></a></p>
|
||||
<h2 id="streams-futures-in-sequence"><a class="header" href="#streams-futures-in-sequence">Streams: Futures in Sequence</a></h2>
|
||||
<p>Recall how we used the receiver for our async channel earlier in this chapter
|
||||
in the <a href="ch17-02-concurrency-with-async.html#message-passing">“Message Passing”</a><!-- ignore --> section. The async
|
||||
<code>recv</code> method produces a sequence of items over time. This is an instance of a
|
||||
much more general pattern known as a <em>stream</em>. Many concepts are naturally
|
||||
represented as streams: items becoming available in a queue, chunks of data
|
||||
being pulled incrementally from the filesystem when the full data set is too
|
||||
large for the computer’s memory, or data arriving over the network over time.
|
||||
Because streams are futures, we can use them with any other kind of future and
|
||||
combine them in interesting ways. For example, we can batch up events to avoid
|
||||
triggering too many network calls, set timeouts on sequences of long-running
|
||||
operations, or throttle user interface events to avoid doing needless work.</p>
|
||||
<p>We saw a sequence of items back in Chapter 13, when we looked at the Iterator
|
||||
trait in <a href="../ch13/ch13-02-iterators.html#the-iterator-trait-and-the-next-method">“The Iterator Trait and the <code>next</code> Method”</a><!--
|
||||
ignore --> section, but there are two differences between iterators and the
|
||||
async channel receiver. The first difference is time: iterators are
|
||||
synchronous, while the channel receiver is asynchronous. The second difference
|
||||
is the API. When working directly with <code>Iterator</code>, we call its synchronous
|
||||
<code>next</code> method. With the <code>trpl::Receiver</code> stream in particular, we called an
|
||||
asynchronous <code>recv</code> method instead. Otherwise, these APIs feel very similar,
|
||||
and that similarity isn’t a coincidence. A stream is like an asynchronous form
|
||||
of iteration. Whereas the <code>trpl::Receiver</code> specifically waits to receive
|
||||
messages, though, the general-purpose stream API is much broader: it provides
|
||||
the next item the way <code>Iterator</code> does, but asynchronously.</p>
|
||||
<p>The similarity between iterators and streams in Rust means we can actually
|
||||
create a stream from any iterator. As with an iterator, we can work with a
|
||||
stream by calling its <code>next</code> method and then awaiting the output, as in Listing
|
||||
17-21, which won’t compile yet.</p>
|
||||
<figure class="listing" id="listing-17-21">
|
||||
<span class="file-name">Filename: src/main.rs</span>
|
||||
<pre><code class="language-rust ignore does_not_compile"><span class="boring">extern crate trpl; // required for mdbook test
|
||||
</span><span class="boring">
|
||||
</span><span class="boring">fn main() {
|
||||
</span><span class="boring"> trpl::block_on(async {
|
||||
</span> let values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
let iter = values.iter().map(|n| n * 2);
|
||||
let mut stream = trpl::stream_from_iter(iter);
|
||||
|
||||
while let Some(value) = stream.next().await {
|
||||
println!("The value was: {value}");
|
||||
}
|
||||
<span class="boring"> });
|
||||
</span><span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-17-21">Listing 17-21</a>: Creating a stream from an iterator and printing its values</figcaption>
|
||||
</figure>
|
||||
<p>We start with an array of numbers, which we convert to an iterator and then
|
||||
call <code>map</code> on to double all the values. Then we convert the iterator into a
|
||||
stream using the <code>trpl::stream_from_iter</code> function. Next, we loop over the
|
||||
items in the stream as they arrive with the <code>while let</code> loop.</p>
|
||||
<p>Unfortunately, when we try to run the code, it doesn’t compile but instead
|
||||
reports that there’s no <code>next</code> method available:</p>
|
||||
<!-- manual-regeneration
|
||||
cd listings/ch17-async-await/listing-17-21
|
||||
cargo build
|
||||
copy only the error output
|
||||
-->
|
||||
<pre><code class="language-text">error[E0599]: no method named `next` found for struct `tokio_stream::iter::Iter` in the current scope
|
||||
--> src/main.rs:10:40
|
||||
|
|
||||
10 | while let Some(value) = stream.next().await {
|
||||
| ^^^^
|
||||
|
|
||||
= help: items from traits can only be used if the trait is in scope
|
||||
help: the following traits which provide `next` are implemented but not in scope; perhaps you want to import one of them
|
||||
|
|
||||
1 + use crate::trpl::StreamExt;
|
||||
|
|
||||
1 + use futures_util::stream::stream::StreamExt;
|
||||
|
|
||||
1 + use std::iter::Iterator;
|
||||
|
|
||||
1 + use std::str::pattern::Searcher;
|
||||
|
|
||||
help: there is a method `try_next` with a similar name
|
||||
|
|
||||
10 | while let Some(value) = stream.try_next().await {
|
||||
| ~~~~~~~~
|
||||
</code></pre>
|
||||
<p>As this output explains, the reason for the compiler error is that we need the
|
||||
right trait in scope to be able to use the <code>next</code> method. Given our discussion
|
||||
so far, you might reasonably expect that trait to be <code>Stream</code>, but it’s
|
||||
actually <code>StreamExt</code>. Short for <em>extension</em>, <code>Ext</code> is a common pattern in the
|
||||
Rust community for extending one trait with another.</p>
|
||||
<p>The <code>Stream</code> trait defines a low-level interface that effectively combines the
|
||||
<code>Iterator</code> and <code>Future</code> traits. <code>StreamExt</code> supplies a higher-level set of APIs
|
||||
on top of <code>Stream</code>, including the <code>next</code> method as well as other utility
|
||||
methods similar to those provided by the <code>Iterator</code> trait. <code>Stream</code> and
|
||||
<code>StreamExt</code> are not yet part of Rust’s standard library, but most ecosystem
|
||||
crates use similar definitions.</p>
|
||||
<p>The fix to the compiler error is to add a <code>use</code> statement for
|
||||
<code>trpl::StreamExt</code>, as in Listing 17-22.</p>
|
||||
<figure class="listing" id="listing-17-22">
|
||||
<span class="file-name">Filename: src/main.rs</span>
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">extern crate trpl; // required for mdbook test
|
||||
</span><span class="boring">
|
||||
</span>use trpl::StreamExt;
|
||||
|
||||
fn main() {
|
||||
trpl::block_on(async {
|
||||
let values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
// --snip--
|
||||
<span class="boring"> let iter = values.iter().map(|n| n * 2);
|
||||
</span><span class="boring"> let mut stream = trpl::stream_from_iter(iter);
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> while let Some(value) = stream.next().await {
|
||||
</span><span class="boring"> println!("The value was: {value}");
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring"> });
|
||||
</span><span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-17-22">Listing 17-22</a>: Successfully using an iterator as the basis for a stream</figcaption>
|
||||
</figure>
|
||||
<p>With all those pieces put together, this code works the way we want! What’s
|
||||
more, now that we have <code>StreamExt</code> in scope, we can use all of its utility
|
||||
methods, just as with iterators.</p>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user