Fix research tool UI: remove duplicate header, add footer spacing, remove spinner, widen command display
- Remove duplicate tool header (lib.rs already prints it) - Add newline before timing footer for visual separation - Remove spinner animation (incompatible with update_tool_output_line) - Change shell command format to " > `cmd` ..." with 60 char width
This commit is contained in:
793
tmp/adam_streaming_proxy.html
Normal file
793
tmp/adam_streaming_proxy.html
Normal file
@@ -0,0 +1,793 @@
|
||||
<html lang="en"><head><style>
|
||||
.utterances {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
max-width: 760px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.utterances-frame {
|
||||
color-scheme: light;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 1px;
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
}
|
||||
</style>
|
||||
<meta charset="utf-8">
|
||||
<meta name="HandheldFriendly" content="True">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="referrer" content="no-referrer-when-downgrade">
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:site" content="@adam_chal">
|
||||
<meta name="twitter:creator" content="@adam_chal">
|
||||
<meta name="author" content="Adam Chalmers">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:site_name" content="Adam Chalmers Programming Blog">
|
||||
<meta property="og:image" content="https://blog.adamchalmers.com/birds_of_paradise_wide.jpg">
|
||||
<meta property="og:url" content="https://blog.adamchalmers.com/streaming-proxy/">
|
||||
<meta name="og:title" content="Static streams for faster async proxies">
|
||||
<meta name="twitter:title" content="Static streams for faster async proxies">
|
||||
<meta name="description" content="The borrow checker is a tough negotiating partner">
|
||||
<meta name="twitter:description" content="The borrow checker is a tough negotiating partner">
|
||||
<meta property="og:description" content="The borrow checker is a tough negotiating partner">
|
||||
<title>Static streams for faster async proxies</title>
|
||||
<style>
|
||||
/* Basic */
|
||||
@import url('https://fonts.googleapis.com/css?family=Montserrat&display=swap');
|
||||
html {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Montserrat', 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
/* 1 */
|
||||
-ms-text-size-adjust: 100%;
|
||||
/* 2 */
|
||||
-webkit-text-size-adjust: 100%;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: #ffffff;
|
||||
color: hsl(210deg, 2%, 11%);
|
||||
font-size: 18px;
|
||||
line-height: 1.5;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
margin: 0;
|
||||
background: hsl(210deg, 2%, 11%);
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
line-height: 1.5;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
}
|
||||
|
||||
article,
|
||||
aside,
|
||||
details,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
main,
|
||||
menu,
|
||||
nav,
|
||||
section,
|
||||
summary {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 42em;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
main {
|
||||
outline:none;
|
||||
}
|
||||
|
||||
/* Headers */
|
||||
h1 {
|
||||
font-size: 1.35em;
|
||||
}
|
||||
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
h1, h2 {
|
||||
color: #7a9b76;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
h2 {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
/* Links */
|
||||
|
||||
a {
|
||||
color: #5888b8;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:active,
|
||||
a:hover {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
a:hover,
|
||||
a:focus {
|
||||
color: #c64191;
|
||||
border-bottom: 1px solid #c64191;
|
||||
}
|
||||
|
||||
a:active {
|
||||
color: #c64191;
|
||||
opacity: 0.9;
|
||||
border-bottom: 1px solid #c64191;
|
||||
}
|
||||
|
||||
a.active {
|
||||
color: #c64191;
|
||||
}
|
||||
|
||||
a.skip-main {
|
||||
left:-999px;
|
||||
position:absolute;
|
||||
top:auto;
|
||||
width:1px;
|
||||
height:1px;
|
||||
overflow:hidden;
|
||||
z-index:-999;
|
||||
}
|
||||
|
||||
a.skip-main:focus,
|
||||
a.skip-main:active {
|
||||
left: auto;
|
||||
top: 0px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
overflow:auto;
|
||||
z-index:999;
|
||||
padding: 4px 6px 4px 6px;
|
||||
text-decoration: underline;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Table */
|
||||
.table-wrapper {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
table {
|
||||
max-width: 100%;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
thead {
|
||||
background: lightgrey;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 0.5em 1em;
|
||||
border: 1px double lightgrey;
|
||||
}
|
||||
|
||||
/* Code */
|
||||
pre {
|
||||
padding: 1em;
|
||||
background-color: #f1f1f1;
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
code,
|
||||
pre,
|
||||
kbd {
|
||||
font-family: monospace;
|
||||
font-size: 0.90em;
|
||||
line-height: 154%;
|
||||
}
|
||||
|
||||
code {
|
||||
color: #c78baf
|
||||
}
|
||||
|
||||
/* Styles */
|
||||
|
||||
blockquote {
|
||||
border-left: 2px solid #cccccc;
|
||||
padding: 0.1em 1em;
|
||||
margin-left: 0.75em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
hr {
|
||||
height: 1px;
|
||||
border: 0;
|
||||
border-top: 1px solid #cccccc;
|
||||
}
|
||||
|
||||
ul ol, ol ol, ul ul {
|
||||
margin: 0em 2em;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 1em;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
font-size: 1em;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
header nav {
|
||||
margin-top: 1em;
|
||||
max-width: 100%;
|
||||
text-align: right;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
header nav a {
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
header a {
|
||||
color: hsl(210deg, 2%, 11%);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
header a {
|
||||
color: #7a9b76;
|
||||
}
|
||||
}
|
||||
|
||||
.site-header {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Pages */
|
||||
main h1 {
|
||||
margin-top: 1em;
|
||||
font-weight: normal;
|
||||
line-height: 1.1em;
|
||||
margin-bottom: 0.5em;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.post-short-list:first-of-type {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
/* Articles */
|
||||
|
||||
|
||||
article:not(:last-of-type) {
|
||||
border-bottom: thin solid #f1f1f1;
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
|
||||
article header h1 {
|
||||
font-size: 1.35em;
|
||||
line-height: 1.1em;
|
||||
margin-bottom: 0.5em;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
article header h1 a {
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
color: hsl(210deg, 2%, 11%);
|
||||
}
|
||||
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
article header h1 a {
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
color: #7a9b76;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.article-info {
|
||||
font-size: 0.75em;
|
||||
color: grey;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.article-info a {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
.article-info a:hover {
|
||||
color: #c64191;
|
||||
}
|
||||
|
||||
.post-short-list .article-info {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.article-taxonomies {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.article-date {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.article-categories {
|
||||
display: inline;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.article-categories li {
|
||||
display: inline;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.article-tags {
|
||||
display: inline;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.article-tags li {
|
||||
display: inline;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
article img {
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
height: auto;
|
||||
margin: 0 auto .5em;
|
||||
}
|
||||
|
||||
.read-more {
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
.divider {
|
||||
display: block;
|
||||
height: 1px;
|
||||
border: 0;
|
||||
border-top: thin solid #f1f1f1;
|
||||
width: 25%;
|
||||
margin: 1em auto;
|
||||
}
|
||||
|
||||
.post-summary {
|
||||
margin-top: 0.5em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.post-summary > p {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Other pages */
|
||||
.terms {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
line-height: 2em;
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 3em;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pagination-item {
|
||||
background: #fafafa;
|
||||
padding: 0.75em 0.75em;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.pagination-item a {
|
||||
color: hsl(210deg, 2%, 11%)333;
|
||||
}
|
||||
|
||||
.pagination-item a:hover, .pagination-item a:focus {
|
||||
color: #c64191;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
|
||||
footer {
|
||||
border-top: thin solid #f1f1f1;
|
||||
margin-top: 3em;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
ul.language-select {
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
ul.language-select > li {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
/* Media Queries */
|
||||
|
||||
@media (max-width: 840px) {
|
||||
.main-wrapper {
|
||||
margin: 0;
|
||||
max-width: none;
|
||||
overflow-x: hidden;
|
||||
padding-left: 25px;
|
||||
padding-right: 25px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 90%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.pagination-item {
|
||||
padding: 0.5em 0.5em;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
header {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.site-header {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
header nav {
|
||||
margin-top: 1em;
|
||||
max-width: 100%;
|
||||
text-align: center;
|
||||
background: #fafafa;
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
header nav {
|
||||
margin-top: 1em;
|
||||
max-width: 100%;
|
||||
text-align: center;
|
||||
background: #232323;
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
}
|
||||
|
||||
header nav a:first-of-type {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
header nav a {
|
||||
margin-left: 5%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<link href="https://hachyderm.io/@adam_chal" rel="me">
|
||||
<link rel="shortcut icon" href="https://blog.adamchalmers.com/favicon.ico">
|
||||
|
||||
|
||||
<link rel="alternate" type="application/atom+xml" title="RSS" href="https://blog.adamchalmers.com/atom.xml">
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip-main" href="#main">Skip to content</a>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1 class="site-header">
|
||||
<a href="https://blog.adamchalmers.com">Adam Chalmers</a>
|
||||
</h1>
|
||||
<nav>
|
||||
|
||||
|
||||
|
||||
<a href="https://blog.adamchalmers.com/about/">About me</a>
|
||||
|
||||
|
||||
<a href="https://blog.adamchalmers.com/tags/video/">Video</a>
|
||||
|
||||
|
||||
<a href="https://blog.adamchalmers.com/tags/audio/">Audio</a>
|
||||
|
||||
|
||||
<a href="https://blog.adamchalmers.com/tags/">Tags</a>
|
||||
|
||||
|
||||
<a href="https://blog.adamchalmers.com/atom.xml">Atom feed</a>
|
||||
|
||||
|
||||
</nav>
|
||||
</header>
|
||||
<main id="main" tabindex="-1">
|
||||
|
||||
|
||||
<article class="post">
|
||||
<header>
|
||||
<h1>Static streams for faster async proxies</h1>
|
||||
</header>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>
|
||||
<a href="https://blog.adamchalmers.com/streaming-proxy/#why-streaming-instead-of-buffering">Why streaming instead of buffering?</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="https://blog.adamchalmers.com/streaming-proxy/#the-problem">The problem</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="https://blog.adamchalmers.com/streaming-proxy/#the-solution">The solution</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="https://blog.adamchalmers.com/streaming-proxy/#benchmarks">Benchmarks</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="https://blog.adamchalmers.com/streaming-proxy/#takeaways">Takeaways</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<div class="content">
|
||||
<p>Last week in <a href="https://discord.com/invite/tokio">Tokio's Discord chat server</a> someone asked a really interesting question: how can you stream a multipart body from an incoming request into an outgoing request? My struggle to answer this really helped me understand Rust async lifetimes. We'll explore <em>why</em> this is tricky, then design and benchmark a solution. </p>
|
||||
<span id="continue-reading"></span>
|
||||
<p>Note: all the code is in a full <a href="https://github.com/adamchalmers/axum-reqwest">GitHub example</a>.</p>
|
||||
<hr>
|
||||
<p>The question was:</p>
|
||||
<blockquote>
|
||||
<p>I'm rather new to Rust, so perhaps I'm trying to bite off more than I can chew, but I can't find any way to stream an <code>axum::extract::multipart::Field</code> to the body of a PUT request made with reqwest. Obviously, waiting for all the bytes works just fine, as in this test handler:</p>
|
||||
</blockquote>
|
||||
<pre style="background-color:#2b303b;"><code class="language-rust" data-lang="rust"><span style="color:#b48ead;">use </span><span style="color:#c0c5ce;">axum::{extract, Extension};
|
||||
</span><span style="color:#b48ead;">use </span><span style="color:#c0c5ce;">reqwest::StatusCode;
|
||||
|
||||
</span><span style="color:#65737e;">/// An Axum request handler.
|
||||
</span><span style="color:#c0c5ce;">async </span><span style="color:#b48ead;">fn </span><span style="color:#8fa1b3;">upload</span><span style="color:#c0c5ce;">(
|
||||
</span><span style="color:#65737e;">// The request body should be a Multipart.
|
||||
</span><span style="color:#b48ead;">mut </span><span style="color:#bf616a;">multipart</span><span style="color:#c0c5ce;">: extract::Multipart,
|
||||
</span><span style="color:#65737e;">// The server should set a reqwest::Client in the extensions.
|
||||
</span><span style="color:#c0c5ce;"> Extension(</span><span style="color:#bf616a;">client</span><span style="color:#c0c5ce;">): Extension<reqwest::Client>,
|
||||
</span><span style="color:#65737e;">// If this function succeeds, return HTTP 200 and the Ok string as the body.
|
||||
// If it fails, return the given status code and the Err string as the body.
|
||||
</span><span style="color:#c0c5ce;">) -> Result<String, (StatusCode, String)> {
|
||||
|
||||
</span><span style="color:#65737e;">// Get the first field of the multipart request.
|
||||
</span><span style="color:#b48ead;">if let </span><span style="color:#c0c5ce;">Some(field) = multipart.</span><span style="color:#96b5b4;">next_field</span><span style="color:#c0c5ce;">().await.</span><span style="color:#96b5b4;">unwrap</span><span style="color:#c0c5ce;">() {
|
||||
client
|
||||
.</span><span style="color:#96b5b4;">put</span><span style="color:#c0c5ce;">(url::Url::parse("</span><span style="color:#a3be8c;">https://example.com/file</span><span style="color:#c0c5ce;">").</span><span style="color:#96b5b4;">unwrap</span><span style="color:#c0c5ce;">())
|
||||
.</span><span style="color:#96b5b4;">body</span><span style="color:#c0c5ce;">(field.</span><span style="color:#96b5b4;">bytes</span><span style="color:#c0c5ce;">().await.</span><span style="color:#96b5b4;">unwrap</span><span style="color:#c0c5ce;">())
|
||||
.</span><span style="color:#96b5b4;">send</span><span style="color:#c0c5ce;">()
|
||||
.await
|
||||
.</span><span style="color:#96b5b4;">map_err</span><span style="color:#c0c5ce;">(|_| (StatusCode::</span><span style="color:#d08770;">INTERNAL_SERVER_ERROR</span><span style="color:#c0c5ce;">, "</span><span style="color:#a3be8c;">Oops</span><span style="color:#c0c5ce;">".</span><span style="color:#96b5b4;">to_string</span><span style="color:#c0c5ce;">()))
|
||||
.</span><span style="color:#96b5b4;">map</span><span style="color:#c0c5ce;">(|_| "</span><span style="color:#a3be8c;">Yay</span><span style="color:#c0c5ce;">".</span><span style="color:#96b5b4;">to_string</span><span style="color:#c0c5ce;">())
|
||||
} </span><span style="color:#b48ead;">else </span><span style="color:#c0c5ce;">{
|
||||
Err((StatusCode::</span><span style="color:#d08770;">INTERNAL_SERVER_ERROR</span><span style="color:#c0c5ce;">, "</span><span style="color:#a3be8c;">Oh no</span><span style="color:#c0c5ce;">".</span><span style="color:#96b5b4;">to_string</span><span style="color:#c0c5ce;">()))
|
||||
}
|
||||
}
|
||||
</span></code></pre>
|
||||
<blockquote>
|
||||
<p>Everything I've found for using a stream as the request body requires that the stream be 'static, which of course the fields I'm getting from multipart aren't.</p>
|
||||
</blockquote>
|
||||
<p>The problem sounded very simple -- the server should stream a request in, and stream a request out -- so I figured I'd help this person out, for two reasons:</p>
|
||||
<ol>
|
||||
<li>It's nice to help people and I am Very Nice</li>
|
||||
<li>My dayjob is writing a streaming HTTP proxy in Rust, so this would be good practice.</li>
|
||||
</ol>
|
||||
<p>But it turned out to be harder than I thought. </p>
|
||||
<h1 id="why-streaming-instead-of-buffering">Why streaming instead of buffering?</h1>
|
||||
<p>Why not just buffer the whole body into memory? Is streaming really that important? Well, yeah -- if you're building a proxy, streaming is worth the extra headache, for two reasons:</p>
|
||||
<p>First, <strong>latency</strong> is worse with buffering. Your proxy has to buffer the entire request from the client before sending it to the server. This <em>doubles</em> the latency of requests (assuming all three hosts are equidistant). You have to wait <em>n</em> seconds for the request to reach the proxy, then <em>n</em> seconds to transmit it to the server. But if your proxy used streams, you could get the first few bytes from the client, and send them to the server while you waited for the next few bytes from the client.</p>
|
||||
<p>Second, <strong>memory</strong> overhead is <em>huge</em> with buffering. Say you're proxying a 10gb MacOS system update file. If you buffer every request into memory, your server can't handle many parallel requests (probably <10). But with streaming, you can get the first <em>n</em> bytes the client, proxy them to the server, and then free them. No more crashing the process because it ran out of memory.</p>
|
||||
<p>So we <em>really</em> want our proxy server to stream requests. There's just one problem. Our HTTP server and HTTP client seem to have contradictory lifetimes.</p>
|
||||
<h1 id="the-problem">The problem</h1>
|
||||
<p>Axum's <a href="https://docs.rs/axum/latest/axum/extract/struct.Multipart.html"><code>Multipart</code> extractor</a> owns the incoming request body. It has a method <a href="https://docs.rs/axum/latest/axum/extract/struct.Multipart.html#method.next_field"><code>next_field</code></a> that returns a <a href="https://docs.rs/axum/latest/axum/extract/multipart/struct.Field.html"><code>Field</code></a> type. That <code>Field</code> is a view into the data from the main <code>Multipart</code> body. It just borrows part of the multipart data. This means the Field is actually <code>Field<'a></code> where <code>'a</code> is the lifetime of the <code>Multipart</code> that created it.</p>
|
||||
<p><img src="/streaming-proxy/field_borrow.jpg" alt="Field borrows from Multipart"></p>
|
||||
<p>Clearly the field can't outlive the Multipart, because then Field would be pointing at data that is no longer there. Rust borrow checker stops us from doing that. So far so good.</p>
|
||||
<p>You can read data from a stream, either by reading the whole thing into memory (as above), or using the Stream trait. This trait is defined by the Futures crate <a href="https://docs.rs/futures/latest/futures/prelude/trait.Stream.html">here</a> and implemented <a href="https://docs.rs/axum/latest/axum/extract/multipart/struct.Field.html#impl-Stream-for-Field%3C%27a%3E">here</a>. Streams are basically asynchronous iterators. When you try to get the next value from them, it might not be ready yet, so you have to <code>.await</code>. This is perfect for reading HTTP request bodies, because you can start processing the body as it comes in piece-by-piece. If the next bytes aren't available, your runtime (e.g. Tokio) will switch to a different task until the data arrives. </p>
|
||||
<p>This means you don't have to wait for the entire body to be available, saving time and maybe RAM, if you don't actually need to store the entire body.</p>
|
||||
<p>So, now we know how to stream data <em>out</em> of the body. What about streaming data <em>into</em> a new request's body?</p>
|
||||
<p>We'll use <a href="docs.rs/reqwest">reqwest</a> as the HTTP client. Reqwest can send various things as HTTP bodies -- strings, vectors of bytes, and even streams, using the <a href="https://docs.rs/reqwest/latest/reqwest/struct.Body.html#method.wrap_stream"><code>wrap_stream</code></a> method. The problem is, the stream has to be <code>'static</code>, a special lifetime which means "either it's not borrowed, or it's borrowed for the entire length of the program". This means we can't use <code>Field</code> as a reqwest body, because it's not <code>'static</code>. It's borrowed, and it's only valid for as long as the <code>Multipart</code> that owns it.</p>
|
||||
<p>Why does reqwest only allow static streams to be bodies? I asked its creator <a href="https://twitter.com/seanmonstar">Sean McArthur</a>.</p>
|
||||
<blockquote>
|
||||
<p>Sean: Internally the connection is in a separate task, to manage connection-level stuff (keep alive, if http2: pings, settings, and flow control). The body gets sent to that connection task</p>
|
||||
</blockquote>
|
||||
<blockquote>
|
||||
<p>Adam: So basically the connection task takes ownership of all request bodies? And therefore bodies have to be 'static because they need to be moved into that task? </p>
|
||||
</blockquote>
|
||||
<blockquote>
|
||||
<p>Sean: Exactly</p>
|
||||
</blockquote>
|
||||
<p>But there <em>is</em> a solution. The field type is <code>Field<'a></code>, so it's generic over various possible lifetimes. We just need to make sure that the <em>specific</em> lifetime our server uses is <code>'static</code>.</p>
|
||||
<h1 id="the-solution">The solution</h1>
|
||||
<p>Note that the <code>Multipart</code> object itself is <code>'static</code>. Why? Because it's not borrowed. The <code>next_field</code> type signature is <code>next_field(&mut self) -> Field<'_></code>. What's that <code>'_</code>? That's an <em>elided lifetime</em>. Basically these two signatures are equivalent:</p>
|
||||
<pre style="background-color:#2b303b;"><code class="language-rust" data-lang="rust"><span style="color:#b48ead;">fn </span><span style="color:#8fa1b3;">next_field</span><span style="color:#c0c5ce;">(&</span><span style="color:#b48ead;">'a mut </span><span style="color:#bf616a;">self</span><span style="color:#c0c5ce;">) -> Field<</span><span style="color:#b48ead;">'a</span><span style="color:#c0c5ce;">>
|
||||
fn next_field(&</span><span style="color:#b48ead;">mut</span><span style="color:#c0c5ce;"> self) -> Field<'_>
|
||||
</span></code></pre>
|
||||
<p>So, the field type is <code>Field<'a></code>, but that <code>'a</code> is generic. Its definition works for any possible lifetime. We just have to make sure that when our server's handler is compiled, it knows that <code>'a</code> is <code>'static</code>. </p>
|
||||
<p>The key insight is that, if you want to use a stream as a reqwest body, it can't borrow anything. Because if it borrowed data from some other thread which owns data, what happens if the owning thread dies? Your body would keep reading from the freed memory, and Rust won't let that happen. This means <em>the stream has to own all the data being streamed</em>. </p>
|
||||
<p>So, the stream needs to own the <code>Multipart</code>! After all, the multipart owns the data, and the stream has to own the data. So the stream has to own the multipart.</p>
|
||||
<p>Once I realized that, the solution emerged. We'll define a new type of Stream that owns a Multipart and streams data out of its fields.</p>
|
||||
<pre style="background-color:#2b303b;"><code class="language-rust" data-lang="rust"><span style="color:#b48ead;">use </span><span style="color:#c0c5ce;">axum::{body::Bytes, extract, extract::multipart::MultipartError};
|
||||
</span><span style="color:#b48ead;">use </span><span style="color:#c0c5ce;">futures::prelude::Stream;
|
||||
|
||||
</span><span style="color:#65737e;">/// Wrapper for axum's Multipart type.
|
||||
</span><span style="color:#b48ead;">struct </span><span style="color:#c0c5ce;">MultipartStream(extract::Multipart);
|
||||
|
||||
</span><span style="color:#b48ead;">impl </span><span style="color:#c0c5ce;">MultipartStream {
|
||||
</span><span style="color:#65737e;">/// Stream every byte from every field.
|
||||
</span><span style="color:#b48ead;">fn </span><span style="color:#8fa1b3;">into_stream</span><span style="color:#c0c5ce;">(</span><span style="color:#b48ead;">mut </span><span style="color:#bf616a;">self</span><span style="color:#c0c5ce;">) -> impl Stream<Item = Result<Bytes, MultipartError>> {
|
||||
</span><span style="color:#65737e;">// Create a stream using crates.io/crates/async-stream
|
||||
</span><span style="color:#c0c5ce;">async_stream::stream! {
|
||||
</span><span style="color:#b48ead;">while let </span><span style="color:#c0c5ce;">Some(field) = </span><span style="color:#bf616a;">self</span><span style="color:#c0c5ce;">.</span><span style="color:#d08770;">0.</span><span style="color:#96b5b4;">next_field</span><span style="color:#c0c5ce;">().await.</span><span style="color:#96b5b4;">unwrap</span><span style="color:#c0c5ce;">() {
|
||||
</span><span style="color:#65737e;">// Special syntax from the stream! macro.
|
||||
// Basically streams items out of the `field` stream.
|
||||
</span><span style="color:#b48ead;">for</span><span style="color:#c0c5ce;"> await value in field {
|
||||
</span><span style="background-color:#bf616a;color:#2b303b;">yield</span><span style="color:#c0c5ce;"> value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</span></code></pre>
|
||||
<p>We make a <a href="https://doc.rust-lang.org/rust-by-example/generics/new_types.html">newtype</a> for converting a Multipart into a Stream. I'm Very Creative so I chose the imaginative name MultipartStream. It owns the Multipart, and its <code>into_stream</code> method consumes (aka takes ownership of) <code>self</code>, so that method also owns the Multipart. This means the Multipart is <code>'static</code>. The method creates a stream, using the <code>stream!</code> macro from the <a href="https://docs.rs/async-stream/latest/async_stream/">async-stream</a> crate. And that stream then takes ownership of <code>self</code> and therefore the Multipart.</p>
|
||||
<p>All this means that the stream doesn't borrow anything. It owns all its data -- both the multipart and its fields -- so the stream is <code>'static</code>. Now you can pass it to <code>reqweset::Body::wrap_stream</code>. </p>
|
||||
<p>Note: the <code>into_stream</code> example is very rudimentary -- it concatenates all the fields together. This probably wouldn't be useful. In a real server you might want to only stream certain fields, or maybe filter out fields, or maybe only stream the <em>nth</em> field. </p>
|
||||
<p>The last thing to do is actually <em>use</em> this <code>MultipartStream</code> wrapper in our endpoint. </p>
|
||||
<pre style="background-color:#2b303b;"><code class="language-rust" data-lang="rust"><span style="color:#65737e;">/// State for the proxy server.
|
||||
/// See https://docs.rs/axum/0.6.0-rc.1/axum/extract/struct.State.html
|
||||
</span><span style="color:#c0c5ce;">#[</span><span style="color:#bf616a;">derive</span><span style="color:#c0c5ce;">(Clone)]
|
||||
</span><span style="color:#b48ead;">struct </span><span style="color:#c0c5ce;">ProxyState {
|
||||
</span><span style="color:#bf616a;">dst_port</span><span style="color:#c0c5ce;">: </span><span style="color:#b48ead;">u16</span><span style="color:#c0c5ce;">,
|
||||
</span><span style="color:#bf616a;">client</span><span style="color:#c0c5ce;">: reqwest::Client,
|
||||
}
|
||||
|
||||
async </span><span style="color:#b48ead;">fn </span><span style="color:#8fa1b3;">proxy_upload_streaming</span><span style="color:#c0c5ce;">(
|
||||
</span><span style="color:#65737e;">// the State parameter can be destructed right here in the function signature, so let's
|
||||
// destructure it and unpack its two fields.
|
||||
</span><span style="color:#c0c5ce;"> State(</span><span style="color:#bf616a;">ProxyState</span><span style="color:#c0c5ce;"> { </span><span style="color:#bf616a;">dst_port</span><span style="color:#c0c5ce;">, </span><span style="color:#bf616a;">client</span><span style="color:#c0c5ce;"> }): State<ProxyState>,
|
||||
</span><span style="color:#65737e;">// The incoming request's multipart body.
|
||||
</span><span style="color:#bf616a;">incoming_body</span><span style="color:#c0c5ce;">: extract::Multipart,
|
||||
) -> Result<String, (StatusCode, String)> {
|
||||
|
||||
</span><span style="color:#65737e;">// Stream the incoming request's body, into the outgoing request's body.
|
||||
</span><span style="color:#b48ead;">let</span><span style="color:#c0c5ce;"> stream = MultipartStream(incoming_body).</span><span style="color:#96b5b4;">into_stream</span><span style="color:#c0c5ce;">();
|
||||
</span><span style="color:#b48ead;">let</span><span style="color:#c0c5ce;"> outgoing_body = reqwest::Body::wrap_stream(stream);
|
||||
|
||||
</span><span style="color:#65737e;">// Send the outgoing request
|
||||
</span><span style="color:#c0c5ce;"> client
|
||||
.</span><span style="color:#96b5b4;">post</span><span style="color:#c0c5ce;">(url::Url::parse(&format!("</span><span style="color:#a3be8c;">http://127.0.0.1:</span><span style="color:#d08770;">{dst_port}</span><span style="color:#a3be8c;">/</span><span style="color:#c0c5ce;">")).</span><span style="color:#96b5b4;">unwrap</span><span style="color:#c0c5ce;">())
|
||||
.</span><span style="color:#96b5b4;">body</span><span style="color:#c0c5ce;">(outgoing_body)
|
||||
.</span><span style="color:#96b5b4;">send</span><span style="color:#c0c5ce;">()
|
||||
.await
|
||||
.</span><span style="color:#96b5b4;">map_err</span><span style="color:#c0c5ce;">(|</span><span style="color:#bf616a;">e</span><span style="color:#c0c5ce;">| (StatusCode::</span><span style="color:#d08770;">INTERNAL_SERVER_ERROR</span><span style="color:#c0c5ce;">, e.</span><span style="color:#96b5b4;">to_string</span><span style="color:#c0c5ce;">()))
|
||||
.</span><span style="color:#96b5b4;">map</span><span style="color:#c0c5ce;">(|_| format!("</span><span style="color:#a3be8c;">All OK</span><span style="color:#96b5b4;">\n</span><span style="color:#c0c5ce;">"))
|
||||
}
|
||||
</span></code></pre>
|
||||
<p>Note, this example uses Axum 0.6.0-rc.1 with its new <a href="https://docs.rs/axum/0.6.0-rc.1/axum/extract/struct.State.html">State</a> types. It's possible that it might change a little before the final 0.6 release. See <a href="https://tokio.rs/blog/2022-08-whats-new-in-axum-0-6-0-rc1">the announcement</a> for more. State is basically like an Axum extension where the compiler can guarantee it's always set. This perfectly solves the problem my <a href="/what-are-extensions">previous post about Axum</a> complained about, where <em>I</em> know the extension is always set, but the compiler doesn't, so I need a dubious <code>.unwrap()</code></p>
|
||||
<h1 id="benchmarks">Benchmarks</h1>
|
||||
<p>I ran some benchmarks on the repo. Basically I used <code>curl</code> to send the proxy server a Multipart body with 20 copies of the Unix wordlist. Then the server proxied it to a second server, which prints it. I compared the streaming proxy above, with a proxy that buffers everything. You can see the full setup in the <a href="https://github.com/adamchalmers/axum-reqwest">GitHub example</a>. </p>
|
||||
<p>Because I ran this all locally, I don't expect much difference in total time. After all, the latency between processes running on my Macbook is pretty low. So doubling the latency won't matter much, because the latency is nearly zero anyway. But I expect the RAM usage to be very different.</p>
|
||||
<table><thead><tr><th></th><th>Time (seconds)</th><th>RAM (mb)</th></tr></thead><tbody>
|
||||
<tr><td>Streaming</td><td>0.10</td><td>16</td></tr>
|
||||
<tr><td>Buffering</td><td>0.32</td><td>128</td></tr>
|
||||
</tbody></table>
|
||||
<p>Yep, streaming saves a lot of memory.</p>
|
||||
<h1 id="takeaways">Takeaways</h1>
|
||||
<ul>
|
||||
<li>reqwest connections are handled in their own thread, so they need to own their bodies.
|
||||
<ul>
|
||||
<li>This is just a design choice -- other HTTP libraries could work differently, although <a href="https://docs.rs/tide">tide</a> and <a href="https://docs.rs/awc">actix web client</a> also require streaming bodies be <code>'static</code>. </li>
|
||||
<li>I think this is partly because of the Tokio runtime, and other runtimes might not require 'static, see <a href="https://www.youtube.com/watch?v=PbgTyCSDPrs">this talk</a> from the creator of <a href="https://docs.rs/glommio/latest/glommio/">glommio</a>.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><code>'static</code> means "this value either isn't borrowed, or is borrowed for the entire runtime of the program"</li>
|
||||
<li>A static stream can't borrow any data</li>
|
||||
<li>Implementing your own streams with <a href="https://docs.rs/async-stream/latest/async_stream/">async-stream</a> is pretty easy</li>
|
||||
</ul>
|
||||
<hr>
|
||||
|
||||
</div>
|
||||
<div class="utterances">
|
||||
<iframe class="utterances-frame" title="Comments" scrolling="no" src="https://utteranc.es/utterances.html?src=https%3A%2F%2Futteranc.es%2Fclient.js&repo=adamchalmers%2Fblog&issue-term=title&label=comments&theme=github-light&crossorigin=anonymous&async=&url=https%3A%2F%2Fblog.adamchalmers.com%2Fstreaming-proxy%2F&origin=https%3A%2F%2Fblog.adamchalmers.com&pathname=streaming-proxy%2F&title=Static+streams+for+faster+async+proxies&description=The+borrow+checker+is+a+tough+negotiating+partner&og%3Atitle=Static+streams+for+faster+async+proxies&session=" loading="lazy"></iframe>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="article-info">
|
||||
|
||||
<div class="article-date">26 August 2022</div>
|
||||
|
||||
<div class="article-taxonomies">
|
||||
|
||||
|
||||
<ul class="article-tags">
|
||||
|
||||
<li><a href="https://blog.adamchalmers.com/tags/rust/">#rust</a></li>
|
||||
|
||||
<li><a href="https://blog.adamchalmers.com/tags/programming/">#programming</a></li>
|
||||
|
||||
<li><a href="https://blog.adamchalmers.com/tags/web/">#web</a></li>
|
||||
|
||||
<li><a href="https://blog.adamchalmers.com/tags/streams/">#streams</a></li>
|
||||
|
||||
<li><a href="https://blog.adamchalmers.com/tags/axum/">#axum</a></li>
|
||||
|
||||
<li><a href="https://blog.adamchalmers.com/tags/multipart/">#multipart</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</article>
|
||||
|
||||
|
||||
</main>
|
||||
<footer>
|
||||
<p>
|
||||
© Adam Chalmers 2026<br>
|
||||
Powered by <a target="_blank" href="https://getzola.org/">Zola</a>, Theme <a target="_blank" href="https://github.com/zbrox/anpu-zola-theme">Anpu</a>.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
<!-- Cloudflare Pages Analytics --><script defer="" src="https://static.cloudflareinsights.com/beacon.min.js" data-cf-beacon="{"token": "77db9d734c8d4d1e8d86c8008c5e9dc6"}"></script><!-- Cloudflare Pages Analytics --><script defer="" src="https://static.cloudflareinsights.com/beacon.min.js/vcd15cbe7772f49c399c6a5babf22c1241717689176015" integrity="sha512-ZpsOmlRQV6y907TI0dKBHq9Md29nnaEIPlkf84rnaERnq6zvWvPUqr2ft8M1aS28oN72PdrCzSjY4U6VaAw1EQ==" data-cf-beacon="{"rayId":"9bb94b571d7ff426","serverTiming":{"name":{"cfExtPri":true,"cfEdge":true,"cfOrigin":true,"cfL4":true,"cfSpeedBrain":true,"cfCacheStatus":true}},"version":"2025.9.1","token":"9350d6ce65a94e1bab85a1247c6f6b0c"}" crossorigin="anonymous"></script>
|
||||
|
||||
|
||||
</body></html>
|
||||
37
tmp/anthropic_best_practices.html
Normal file
37
tmp/anthropic_best_practices.html
Normal file
File diff suppressed because one or more lines are too long
413
tmp/async_stream_docs.html
Normal file
413
tmp/async_stream_docs.html
Normal file
@@ -0,0 +1,413 @@
|
||||
<html lang="en" data-theme="dark" data-docs-rs-theme="dark"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Asynchronous stream of elements."><title>async_stream - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="/-/rustdoc.static/${f}">`).join(""))</script><link rel="preload" as="font" type="font/woff2" href="/-/rustdoc.static/SourceSerif4-Regular-6b053e98.ttf.woff2"><link rel="preload" as="font" type="font/woff2" href="/-/rustdoc.static/FiraSans-Italic-81dc35de.woff2"><link rel="preload" as="font" type="font/woff2" href="/-/rustdoc.static/FiraSans-Regular-0fe48ade.woff2"><link rel="preload" as="font" type="font/woff2" href="/-/rustdoc.static/FiraSans-MediumItalic-ccf7e434.woff2"><link rel="preload" as="font" type="font/woff2" href="/-/rustdoc.static/FiraSans-Medium-e1aa3f0a.woff2"><link rel="preload" as="font" type="font/woff2" href="/-/rustdoc.static/SourceCodePro-Regular-8badfe75.ttf.woff2"><link rel="preload" as="font" type="font/woff2" href="/-/rustdoc.static/SourceCodePro-Semibold-aa29a496.ttf.woff2"><link rel="stylesheet" href="/-/rustdoc.static/normalize-9960930a.css"><link rel="stylesheet" href="/-/static/vendored.css?0-1-0-7bfb09c2-2026-01-03" media="all"><link rel="stylesheet" href="/-/rustdoc.static/rustdoc-80aa586b.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="/-/rustdoc.static/" data-current-crate="async_stream" data-themes="" data-resource-suffix="-20251223-1.94.0-nightly-2ca7bcd03" data-rustdoc-version="1.94.0-nightly (2ca7bcd03 2025-12-23)" data-channel="nightly" data-search-js="search-9e2438ea.js" data-stringdex-js="stringdex-b897f86f.js" data-settings-js="settings-c38705f0.js"><script src="/-/rustdoc.static/storage-e2aeef58.js"></script><script defer="" src="../crates-20251223-1.94.0-nightly-2ca7bcd03.js"></script><script defer="" src="/-/rustdoc.static/main-3e30299d.js"></script><noscript><link rel="stylesheet" href="/-/rustdoc.static/noscript-ffcac47a.css"></noscript><link rel="alternate icon" type="image/png" href="/-/rustdoc.static/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="/-/rustdoc.static/favicon-044be391.svg"><link rel="stylesheet" href="/-/static/rustdoc-2025-08-20.css?0-1-0-7bfb09c2-2026-01-03" media="all"><link rel="stylesheet" href="/-/static/font-awesome.css?0-1-0-7bfb09c2-2026-01-03" media="all">
|
||||
|
||||
<link rel="search" href="/-/static/opensearch.xml" type="application/opensearchdescription+xml" title="Docs.rs">
|
||||
|
||||
<script type="text/javascript">(function() {
|
||||
function applyTheme(theme) {
|
||||
if (theme) {
|
||||
document.documentElement.dataset.docsRsTheme = theme;
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("storage", ev => {
|
||||
if (ev.key === "rustdoc-theme") {
|
||||
applyTheme(ev.newValue);
|
||||
}
|
||||
});
|
||||
|
||||
// see ./storage-change-detection.html for details
|
||||
window.addEventListener("message", ev => {
|
||||
if (ev.data && ev.data.storage && ev.data.storage.key === "rustdoc-theme") {
|
||||
applyTheme(ev.data.storage.value);
|
||||
}
|
||||
});
|
||||
|
||||
applyTheme(window.localStorage.getItem("rustdoc-theme"));
|
||||
})();</script></head><div style="display: none; position: fixed; width: 100%; height: 100%; z-index: 1;"></div><body class="rustdoc-page">
|
||||
<div class="nav-container">
|
||||
<div class="container">
|
||||
<div class="pure-menu pure-menu-horizontal" role="navigation" aria-label="Main navigation">
|
||||
<form action="/releases/search" method="GET" id="nav-search-form" class="landing-search-form-nav ">
|
||||
|
||||
|
||||
<a href="/" class="pure-menu-heading pure-menu-link docsrs-logo" aria-label="Docs.rs">
|
||||
<span title="Docs.rs"><span class="fa fa-solid fa-cubes " aria-hidden="true"></span></span>
|
||||
<span class="title">Docs.rs</span>
|
||||
</a><ul class="pure-menu-list">
|
||||
<script id="crate-metadata" type="application/json">
|
||||
|
||||
{
|
||||
"name": "async-stream",
|
||||
"version": "0.3.6"
|
||||
}
|
||||
</script><li class="pure-menu-item pure-menu-has-children">
|
||||
<a href="#" class="pure-menu-link crate-name" title="Asynchronous streams using async & await notation" aria-haspopup="menu">
|
||||
<span class="fa fa-solid fa-cube " aria-hidden="true"></span>
|
||||
<span class="title">async-stream-0.3.6</span>
|
||||
</a><div class="pure-menu-children package-details-menu" role="menu">
|
||||
|
||||
<ul class="pure-menu-list menu-item-divided">
|
||||
<li class="pure-menu-heading" id="crate-title">
|
||||
async-stream 0.3.6
|
||||
<span id="clipboard" class="svg-clipboard" title="Copy crate name and version information"></span>
|
||||
</li><li class="pure-menu-item">
|
||||
<a href="/async-stream/0.3.6/async_stream/" class="pure-menu-link description" id="permalink" title="Get a link to this specific version"><span class="fa fa-solid fa-link " aria-hidden="true"></span> Permalink
|
||||
</a>
|
||||
</li><li class="pure-menu-item">
|
||||
<a href="/crate/async-stream/latest" class="pure-menu-link description" title="See async-stream in docs.rs">
|
||||
<span class="fa fa-solid fa-cube " aria-hidden="true"></span> Docs.rs crate page
|
||||
</a>
|
||||
</li><li class="pure-menu-item">
|
||||
<span class="pure-menu-link description"><span class="fa fa-solid fa-scale-unbalanced-flip " aria-hidden="true"></span>
|
||||
<a href="https://spdx.org/licenses/MIT" class="pure-menu-sublink">MIT</a></span>
|
||||
</li></ul>
|
||||
|
||||
<div class="pure-g menu-item-divided">
|
||||
<div class="pure-u-1-2 right-border">
|
||||
<ul class="pure-menu-list">
|
||||
<li class="pure-menu-heading">Links</li>
|
||||
|
||||
<li class="pure-menu-item">
|
||||
<a href="https://github.com/tokio-rs/async-stream" class="pure-menu-link">
|
||||
<span class="fa fa-solid fa-code-branch " aria-hidden="true"></span> Repository
|
||||
</a>
|
||||
</li><li class="pure-menu-item">
|
||||
<a href="https://crates.io/crates/async-stream" class="pure-menu-link" title="See async-stream in crates.io">
|
||||
<span class="fa fa-solid fa-cube " aria-hidden="true"></span> crates.io
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li class="pure-menu-item">
|
||||
<a href="/crate/async-stream/latest/source/" title="Browse source of async-stream-0.3.6" class="pure-menu-link">
|
||||
<span class="fa fa-solid fa-folder-open " aria-hidden="true"></span> Source
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div><div class="pure-u-1-2">
|
||||
<ul class="pure-menu-list" id="topbar-owners">
|
||||
<li class="pure-menu-heading">Owners</li><li class="pure-menu-item">
|
||||
<a href="https://crates.io/users/carllerche" class="pure-menu-link">
|
||||
<span class="fa fa-solid fa-user " aria-hidden="true"></span> carllerche
|
||||
</a>
|
||||
</li><li class="pure-menu-item">
|
||||
<a href="https://crates.io/teams/github:tokio-rs:core" class="pure-menu-link">
|
||||
<span class="fa fa-solid fa-user " aria-hidden="true"></span> github:tokio-rs:core
|
||||
</a>
|
||||
</li></ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pure-g menu-item-divided">
|
||||
<div class="pure-u-1-2 right-border">
|
||||
<ul class="pure-menu-list">
|
||||
<li class="pure-menu-heading">Dependencies</li>
|
||||
|
||||
|
||||
<li class="pure-menu-item">
|
||||
<div class="pure-menu pure-menu-scrollable sub-menu" tabindex="-1">
|
||||
<ul class="pure-menu-list">
|
||||
<li class="pure-menu-item"><a href="/async-stream-impl/=0.3.6/" class="pure-menu-link">
|
||||
async-stream-impl =0.3.6
|
||||
|
||||
<i class="dependencies normal">normal</i>
|
||||
|
||||
|
||||
</a>
|
||||
</li><li class="pure-menu-item"><a href="/futures-core/^0.3/" class="pure-menu-link">
|
||||
futures-core ^0.3
|
||||
|
||||
<i class="dependencies normal">normal</i>
|
||||
|
||||
|
||||
</a>
|
||||
</li><li class="pure-menu-item"><a href="/pin-project-lite/^0.2/" class="pure-menu-link">
|
||||
pin-project-lite ^0.2
|
||||
|
||||
<i class="dependencies normal">normal</i>
|
||||
|
||||
|
||||
</a>
|
||||
</li><li class="pure-menu-item"><a href="/futures-util/^0.3/" class="pure-menu-link">
|
||||
futures-util ^0.3
|
||||
|
||||
<i class="dependencies dev">dev</i>
|
||||
|
||||
|
||||
</a>
|
||||
</li><li class="pure-menu-item"><a href="/rustversion/^1/" class="pure-menu-link">
|
||||
rustversion ^1
|
||||
|
||||
<i class="dependencies dev">dev</i>
|
||||
|
||||
|
||||
</a>
|
||||
</li><li class="pure-menu-item"><a href="/tokio/^1/" class="pure-menu-link">
|
||||
tokio ^1
|
||||
|
||||
<i class="dependencies dev">dev</i>
|
||||
|
||||
|
||||
</a>
|
||||
</li><li class="pure-menu-item"><a href="/trybuild/^1/" class="pure-menu-link">
|
||||
trybuild ^1
|
||||
|
||||
<i class="dependencies dev">dev</i>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="pure-u-1-2">
|
||||
<ul class="pure-menu-list">
|
||||
<li class="pure-menu-heading">Versions</li>
|
||||
|
||||
<li class="pure-menu-item">
|
||||
<div class="pure-menu pure-menu-scrollable sub-menu" id="releases-list" tabindex="-1" data-url="/crate/async-stream/latest/menus/releases/async_stream/">
|
||||
<span class="rotate"><span class="fa fa-solid fa-spinner " aria-hidden="true"></span></span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="pure-g">
|
||||
<div class="pure-u-1">
|
||||
<ul class="pure-menu-list">
|
||||
<li>
|
||||
<a href="/crate/async-stream/latest" class="pure-menu-link">
|
||||
<b>100%</b>
|
||||
of the crate is documented
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div></div>
|
||||
</li><li class="pure-menu-item pure-menu-has-children">
|
||||
<a href="#" class="pure-menu-link" aria-label="Platform" aria-haspopup="menu">
|
||||
<span class="fa fa-solid fa-gears " aria-hidden="true"></span>
|
||||
<span class="title">Platform</span>
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="pure-menu-children" id="platforms" data-url="/crate/async-stream/latest/menus/platforms/async_stream/" role="menu"><li class="pure-menu-item">
|
||||
<a href="/crate/async-stream/latest/target-redirect/aarch64-apple-darwin/async_stream/" class="pure-menu-link" data-fragment="retain" rel="nofollow">aarch64-apple-darwin</a>
|
||||
</li><li class="pure-menu-item">
|
||||
<a href="/crate/async-stream/latest/target-redirect/aarch64-unknown-linux-gnu/async_stream/" class="pure-menu-link" data-fragment="retain" rel="nofollow">aarch64-unknown-linux-gnu</a>
|
||||
</li><li class="pure-menu-item">
|
||||
<a href="/crate/async-stream/latest/target-redirect/i686-pc-windows-msvc/async_stream/" class="pure-menu-link" data-fragment="retain" rel="nofollow">i686-pc-windows-msvc</a>
|
||||
</li><li class="pure-menu-item">
|
||||
<a href="/crate/async-stream/latest/target-redirect/x86_64-pc-windows-msvc/async_stream/" class="pure-menu-link" data-fragment="retain" rel="nofollow">x86_64-pc-windows-msvc</a>
|
||||
</li><li class="pure-menu-item">
|
||||
<a href="/crate/async-stream/latest/target-redirect/async_stream/" class="pure-menu-link" data-fragment="retain" rel="nofollow">x86_64-unknown-linux-gnu</a>
|
||||
</li></ul>
|
||||
</li><li class="pure-menu-item">
|
||||
<a href="/crate/async-stream/latest/features" title="Browse available feature flags of async-stream-0.3.6" class="pure-menu-link">
|
||||
<span class="fa fa-solid fa-flag " aria-hidden="true"></span>
|
||||
<span class="title">Feature flags</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
</ul><div class="spacer"></div>
|
||||
|
||||
|
||||
|
||||
<ul class="pure-menu-list">
|
||||
<li class="pure-menu-item pure-menu-has-children">
|
||||
<a href="#" class="pure-menu-link" aria-label="docs.rs" aria-haspopup="menu">docs.rs</a>
|
||||
<ul class="pure-menu-children aligned-icons" role="menu"><li class="pure-menu-item"><a class="pure-menu-link" href="/about"><span class="fa fa-solid fa-circle-info " aria-hidden="true"></span> About docs.rs</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/about/badges"><span class="fa fa-brands fa-fonticons " aria-hidden="true"></span> Badges</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/about/builds"><span class="fa fa-solid fa-gears " aria-hidden="true"></span> Builds</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/about/metadata"><span class="fa fa-solid fa-table " aria-hidden="true"></span> Metadata</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/about/redirections"><span class="fa fa-solid fa-road " aria-hidden="true"></span> Shorthand URLs</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/about/download"><span class="fa fa-solid fa-download " aria-hidden="true"></span> Download</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/about/rustdoc-json"><span class="fa fa-solid fa-file-code " aria-hidden="true"></span> Rustdoc JSON</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/releases/queue"><span class="fa fa-solid fa-gears " aria-hidden="true"></span> Build queue</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="https://foundation.rust-lang.org/policies/privacy-policy/#docs.rs" target="_blank"><span class="fa fa-solid fa-shield-halved " aria-hidden="true"></span> Privacy policy</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="pure-menu-list"><li class="pure-menu-item pure-menu-has-children">
|
||||
<a href="#" class="pure-menu-link" aria-label="Rust" aria-haspopup="menu">Rust</a>
|
||||
<ul class="pure-menu-children" role="menu">
|
||||
<li class="pure-menu-item"><a class="pure-menu-link" href="https://www.rust-lang.org/" target="_blank">Rust website</a></li>
|
||||
<li class="pure-menu-item"><a class="pure-menu-link" href="https://doc.rust-lang.org/book/" target="_blank">The Book</a></li>
|
||||
|
||||
<li class="pure-menu-item"><a class="pure-menu-link" href="https://doc.rust-lang.org/std/" target="_blank">Standard Library API Reference</a></li>
|
||||
|
||||
<li class="pure-menu-item"><a class="pure-menu-link" href="https://doc.rust-lang.org/rust-by-example/" target="_blank">Rust by Example</a></li>
|
||||
|
||||
<li class="pure-menu-item"><a class="pure-menu-link" href="https://doc.rust-lang.org/cargo/guide/" target="_blank">The Cargo Guide</a></li>
|
||||
|
||||
<li class="pure-menu-item"><a class="pure-menu-link" href="https://doc.rust-lang.org/nightly/clippy" target="_blank">Clippy Documentation</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div id="search-input-nav">
|
||||
<label for="nav-search">
|
||||
<span class="fa fa-solid fa-magnifying-glass " aria-hidden="true"></span>
|
||||
</label>
|
||||
|
||||
|
||||
|
||||
<input id="nav-search" name="query" type="text" aria-label="Find crate by search query" tabindex="-1" placeholder="Find crate">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div><div class="rustdoc mod crate container-rustdoc" id="rustdoc_body_wrapper" tabindex="-1"><script async="" src="/-/static/menu.js?0-1-0-7bfb09c2-2026-01-03"></script>
|
||||
<script async="" src="/-/static/index.js?0-1-0-7bfb09c2-2026-01-03"></script>
|
||||
|
||||
<iframe src="/-/storage-change-detection.html" width="0" height="0" style="display: none"></iframe><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar>
|
||||
<button class="sidebar-menu-toggle" slot="sidebar-menu-toggle" title="show sidebar">
|
||||
</button>
|
||||
<div class="settings-menu" slot="settings-menu" tabindex="-1">
|
||||
<a href="../settings.html"><span class="label">Settings</span></a>
|
||||
</div>
|
||||
<div class="help-menu" slot="help-menu" tabindex="-1">
|
||||
<a href="../help.html"><span class="label">Help</span></a>
|
||||
</div>
|
||||
<h2><a href="#">Crate async_stream</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../async_stream/index.html">async_<wbr>stream</a><span class="version">0.3.6</span></h2></div><div class="sidebar-elems"><ul class="block"><li><a id="all-types" href="all.html">All Items</a></li></ul><section id="rustdoc-toc"><h3><a href="#">Sections</a></h3><ul class="block top-toc"><li><a href="#usage" title="Usage">Usage</a></li><li><a href="#implementation" title="Implementation">Implementation</a></li></ul><h3><a href="#macros">Crate Items</a></h3><ul class="block"><li><a href="#macros" title="Macros">Macros</a></li></ul></section><div id="rustdoc-modnav"><h3>Crates</h3><ul class="block crate"><li class="current"><a href="../async_stream/index.html">async_stream</a></li></ul></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content"><div class="main-heading"><div id="sidebar-button"><a href="../async_stream/all.html" title="show sidebar"></a></div><h1>Crate <span>async_<wbr>stream</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><span class="sub-heading"><a class="src" href="../src/async_stream/lib.rs.html#1-242">Source</a> </span><rustdoc-toolbar>
|
||||
<div id="search-button" tabindex="-1">
|
||||
<a href="https://docs.rs/async-stream/latest/async_stream/?search="><span class="label">Search</span></a>
|
||||
</div>
|
||||
<div class="settings-menu" tabindex="-1">
|
||||
<a href="../settings.html"><span class="label">Settings</span></a>
|
||||
</div>
|
||||
<div class="help-menu" tabindex="-1">
|
||||
<a href="../help.html"><span class="label">Help</span></a>
|
||||
</div>
|
||||
<button id="toggle-all-docs" title="Collapse sections (shift-click to also collapse impl blocks)"><span class="label">Summary</span></button></rustdoc-toolbar></div><details class="toggle top-doc" open=""><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Asynchronous stream of elements.</p>
|
||||
<p>Provides two macros, <code>stream!</code> and <code>try_stream!</code>, allowing the caller to
|
||||
define asynchronous streams of elements. These are implemented using <code>async</code>
|
||||
& <code>await</code> notation. This crate works without unstable features.</p>
|
||||
<p>The <code>stream!</code> macro returns an anonymous type implementing the <a href="https://docs.rs/futures-core/*/futures_core/stream/trait.Stream.html"><code>Stream</code></a>
|
||||
trait. The <code>Item</code> associated type is the type of the values yielded from the
|
||||
stream. The <code>try_stream!</code> also returns an anonymous type implementing the
|
||||
<a href="https://docs.rs/futures-core/*/futures_core/stream/trait.Stream.html"><code>Stream</code></a> trait, but the <code>Item</code> associated type is <code>Result<T, Error></code>. The
|
||||
<code>try_stream!</code> macro supports using <code>?</code> notation as part of the
|
||||
implementation.</p>
|
||||
<h2 id="usage"><a class="doc-anchor" href="#usage">§</a>Usage</h2>
|
||||
<p>A basic stream yielding numbers. Values are yielded using the <code>yield</code>
|
||||
keyword. The stream block must return <code>()</code>.</p>
|
||||
|
||||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">use </span>async_stream::stream;
|
||||
|
||||
<span class="kw">use </span>futures_util::pin_mut;
|
||||
<span class="kw">use </span>futures_util::stream::StreamExt;
|
||||
|
||||
<span class="attr">#[tokio::main]
|
||||
</span><span class="kw">async fn </span>main() {
|
||||
<span class="kw">let </span>s = <span class="macro">stream!</span> {
|
||||
<span class="kw">for </span>i <span class="kw">in </span><span class="number">0</span>..<span class="number">3 </span>{
|
||||
<span class="kw">yield </span>i;
|
||||
}
|
||||
};
|
||||
|
||||
<span class="macro">pin_mut!</span>(s); <span class="comment">// needed for iteration
|
||||
|
||||
</span><span class="kw">while let </span><span class="prelude-val">Some</span>(value) = s.next().<span class="kw">await </span>{
|
||||
<span class="macro">println!</span>(<span class="string">"got {}"</span>, value);
|
||||
}
|
||||
}</code></pre></div>
|
||||
<p>Streams may be returned by using <code>impl Stream<Item = T></code>:</p>
|
||||
|
||||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">use </span>async_stream::stream;
|
||||
|
||||
<span class="kw">use </span>futures_core::stream::Stream;
|
||||
<span class="kw">use </span>futures_util::pin_mut;
|
||||
<span class="kw">use </span>futures_util::stream::StreamExt;
|
||||
|
||||
<span class="kw">fn </span>zero_to_three() -> <span class="kw">impl </span>Stream<Item = u32> {
|
||||
<span class="macro">stream!</span> {
|
||||
<span class="kw">for </span>i <span class="kw">in </span><span class="number">0</span>..<span class="number">3 </span>{
|
||||
<span class="kw">yield </span>i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<span class="attr">#[tokio::main]
|
||||
</span><span class="kw">async fn </span>main() {
|
||||
<span class="kw">let </span>s = zero_to_three();
|
||||
<span class="macro">pin_mut!</span>(s); <span class="comment">// needed for iteration
|
||||
|
||||
</span><span class="kw">while let </span><span class="prelude-val">Some</span>(value) = s.next().<span class="kw">await </span>{
|
||||
<span class="macro">println!</span>(<span class="string">"got {}"</span>, value);
|
||||
}
|
||||
}</code></pre></div>
|
||||
<p>Streams may be implemented in terms of other streams - <code>async-stream</code> provides <code>for await</code>
|
||||
syntax to assist with this:</p>
|
||||
|
||||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">use </span>async_stream::stream;
|
||||
|
||||
<span class="kw">use </span>futures_core::stream::Stream;
|
||||
<span class="kw">use </span>futures_util::pin_mut;
|
||||
<span class="kw">use </span>futures_util::stream::StreamExt;
|
||||
|
||||
<span class="kw">fn </span>zero_to_three() -> <span class="kw">impl </span>Stream<Item = u32> {
|
||||
<span class="macro">stream!</span> {
|
||||
<span class="kw">for </span>i <span class="kw">in </span><span class="number">0</span>..<span class="number">3 </span>{
|
||||
<span class="kw">yield </span>i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<span class="kw">fn </span>double<S: Stream<Item = u32>>(input: S)
|
||||
-> <span class="kw">impl </span>Stream<Item = u32>
|
||||
{
|
||||
<span class="macro">stream!</span> {
|
||||
<span class="kw">for await </span>value <span class="kw">in </span>input {
|
||||
<span class="kw">yield </span>value * <span class="number">2</span>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<span class="attr">#[tokio::main]
|
||||
</span><span class="kw">async fn </span>main() {
|
||||
<span class="kw">let </span>s = double(zero_to_three());
|
||||
<span class="macro">pin_mut!</span>(s); <span class="comment">// needed for iteration
|
||||
|
||||
</span><span class="kw">while let </span><span class="prelude-val">Some</span>(value) = s.next().<span class="kw">await </span>{
|
||||
<span class="macro">println!</span>(<span class="string">"got {}"</span>, value);
|
||||
}
|
||||
}</code></pre></div>
|
||||
<p>Rust try notation (<code>?</code>) can be used with the <code>try_stream!</code> macro. The <code>Item</code>
|
||||
of the returned stream is <code>Result</code> with <code>Ok</code> being the value yielded and
|
||||
<code>Err</code> the error type returned by <code>?</code>.</p>
|
||||
|
||||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">use </span>tokio::net::{TcpListener, TcpStream};
|
||||
|
||||
<span class="kw">use </span>async_stream::try_stream;
|
||||
<span class="kw">use </span>futures_core::stream::Stream;
|
||||
|
||||
<span class="kw">use </span>std::io;
|
||||
<span class="kw">use </span>std::net::SocketAddr;
|
||||
|
||||
<span class="kw">fn </span>bind_and_accept(addr: SocketAddr)
|
||||
-> <span class="kw">impl </span>Stream<Item = io::Result<TcpStream>>
|
||||
{
|
||||
<span class="macro">try_stream!</span> {
|
||||
<span class="kw">let </span><span class="kw-2">mut </span>listener = TcpListener::bind(addr).<span class="kw">await</span><span class="question-mark">?</span>;
|
||||
|
||||
<span class="kw">loop </span>{
|
||||
<span class="kw">let </span>(stream, addr) = listener.accept().<span class="kw">await</span><span class="question-mark">?</span>;
|
||||
<span class="macro">println!</span>(<span class="string">"received on {:?}"</span>, addr);
|
||||
<span class="kw">yield </span>stream;
|
||||
}
|
||||
}
|
||||
}</code></pre></div><h2 id="implementation"><a class="doc-anchor" href="#implementation">§</a>Implementation</h2>
|
||||
<p>The <code>stream!</code> and <code>try_stream!</code> macros are implemented using proc macros.
|
||||
The macro searches the syntax tree for instances of <code>yield $expr</code> and
|
||||
transforms them into <code>sender.send($expr).await</code>.</p>
|
||||
<p>The stream uses a lightweight sender to send values from the stream
|
||||
implementation to the caller. When entering the stream, an <code>Option<T></code> is
|
||||
stored on the stack. A pointer to the cell is stored in a thread local and
|
||||
<code>poll</code> is called on the async block. When <code>poll</code> returns.
|
||||
<code>sender.send(value)</code> stores the value that cell and yields back to the
|
||||
caller.</p>
|
||||
</div></details><h2 id="macros" class="section-header">Macros<a href="#macros" class="anchor">§</a></h2><dl class="item-table"><dt><a class="macro" href="macro.stream.html" title="macro async_stream::stream">stream</a></dt><dd>Asynchronous stream</dd><dt><a class="macro" href="macro.try_stream.html" title="macro async_stream::try_stream">try_<wbr>stream</a></dt><dd>Asynchronous fallible stream</dd></dl></section><section id="alternative-display" class="content hidden"></section><section id="not-displayed" class="hidden"><section id="search"><div class="main-heading search-results-main-heading"><nav class="sub">
|
||||
<form class="search-form loading">
|
||||
<span></span> <!-- This empty span is a hacky fix for Safari: see #93184 -->
|
||||
<input class="search-input" name="search" aria-label="Run search in the documentation" autocomplete="off" spellcheck="false" placeholder="Type ‘S’ or ‘/’ to search, ‘?’ for more options…" type="search">
|
||||
</form>
|
||||
</nav><div class="search-switcher"></div></div><div class="search-out"></div></section></section></div></main></div></body></html>
|
||||
590
tmp/biriukov_backpressure.html
Normal file
590
tmp/biriukov_backpressure.html
Normal file
@@ -0,0 +1,590 @@
|
||||
<html lang="en-us" dir="ltr"><head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="
|
||||
Async Rust with Tokio I/O Streams: Backpressure, Concurrency, and Ergonomics
|
||||
#
|
||||
|
||||
|
||||
|
||||
Last updated: Oct 2025
|
||||
|
||||
|
||||
Contents
|
||||
|
||||
1 Async Rust with Tokio IO Streams: Backpressure, Concurrency, and Ergonomics
|
||||
|
||||
Backpressure
|
||||
Cancellation
|
||||
|
||||
|
||||
2. I/O loop
|
||||
|
||||
Backpressure propagation
|
||||
Concurrency
|
||||
|
||||
|
||||
3. Tokio I/O Patterns
|
||||
|
||||
TCP split stream
|
||||
Split generic AsyncRead+AsyncWrite stream
|
||||
Bidirectional driver for I/O without split
|
||||
Framed I/O
|
||||
Bidirectional driver for framed I/O
|
||||
|
||||
|
||||
|
||||
|
||||
There are many excellent, straightforward guides for getting started with Async Rust and Tokio. Most focus on the core building blocks: Tokio primitives, Rust futures, and concepts such as Pin/Unpin, and often finish with a simple TCP client-server sample. In fact, the typical tutorial example can usually be reduced to something as simple as:">
|
||||
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#ffffff">
|
||||
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#343a40">
|
||||
<meta name="color-scheme" content="light dark"><meta property="og:url" content="https://biriukov.dev/docs/async-rust-tokio-io/1-async-rust-with-tokio-io-streams-backpressure-concurrency-and-ergonomics/">
|
||||
<meta property="og:site_name" content="Viacheslav Biriukov">
|
||||
<meta property="og:title" content="Async Rust with Tokio I/O Streams: Backpressure, Concurrency, and Ergonomics">
|
||||
<meta property="og:description" content="Async Rust with Tokio I/O Streams: Backpressure, Concurrency, and Ergonomics # Last updated: Oct 2025 Contents
|
||||
1 Async Rust with Tokio IO Streams: Backpressure, Concurrency, and Ergonomics Backpressure Cancellation 2. I/O loop Backpressure propagation Concurrency 3. Tokio I/O Patterns TCP split stream Split generic AsyncRead+AsyncWrite stream Bidirectional driver for I/O without split Framed I/O Bidirectional driver for framed I/O There are many excellent, straightforward guides for getting started with Async Rust and Tokio. Most focus on the core building blocks: Tokio primitives, Rust futures, and concepts such as Pin/Unpin, and often finish with a simple TCP client-server sample. In fact, the typical tutorial example can usually be reduced to something as simple as:">
|
||||
<meta property="og:locale" content="en_us">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="article:section" content="docs">
|
||||
<title>Async Rust with Tokio I/O Streams: Backpressure, Concurrency, and Ergonomics | Viacheslav Biriukov</title>
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="icon" href="/favicon.png">
|
||||
<link rel="stylesheet" href="/book.min.1e13c2d8521416f2409b2e0e47b515c4ee90f2718cddd31ea96d2f739bc9d7e1.css" integrity="sha256-HhPC2FIUFvJAmy4OR7UVxO6Q8nGM3dMeqW0vc5vJ1+E=" crossorigin="anonymous">
|
||||
<script defer="" src="/flexsearch.min.js"></script>
|
||||
<script defer="" src="/en.search.min.dcb69b7f72cf406432b553b1dca142c9cc7ac0f7ea624f296af7cfa987b92fe5.js" integrity="sha256-3Labf3LPQGQytVOx3KFCycx6wPfqYk8pavfPqYe5L+U=" crossorigin="anonymous"></script>
|
||||
<!--
|
||||
Made with Book Theme
|
||||
https://github.com/alex-shpak/hugo-book
|
||||
-->
|
||||
|
||||
<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-599VSLESJL"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag() {
|
||||
dataLayer.push(arguments);
|
||||
}
|
||||
gtag("js", new Date());
|
||||
|
||||
gtag("config", "G-599VSLESJL");
|
||||
</script>
|
||||
|
||||
<link rel="stylesheet" href="/my_css/cookie.css">
|
||||
<link rel="stylesheet" href="/my_css/copy-code.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css">
|
||||
|
||||
</head>
|
||||
<body dir="ltr">
|
||||
<input type="checkbox" class="hidden toggle" id="menu-control">
|
||||
<input type="checkbox" class="hidden toggle" id="toc-control">
|
||||
<main class="container flex">
|
||||
<aside class="book-menu">
|
||||
<div class="book-menu-content">
|
||||
|
||||
<nav>
|
||||
<h2 class="book-brand">
|
||||
<a class="flex align-center" href="/"><span>Viacheslav Biriukov</span>
|
||||
</a>
|
||||
</h2>
|
||||
|
||||
|
||||
<div class="book-search">
|
||||
<input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/">
|
||||
<div class="book-search-spinner hidden"></div>
|
||||
<ul id="book-search-results"></ul>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="book-section-flat">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<span>Async Rust & Tokio I/O Streams</span>
|
||||
|
||||
|
||||
|
||||
<ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/docs/async-rust-tokio-io/1-async-rust-with-tokio-io-streams-backpressure-concurrency-and-ergonomics/" class="active">
|
||||
|
||||
1. Backpressure and Concurrency
|
||||
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/docs/async-rust-tokio-io/2-io-loop/" class="">
|
||||
|
||||
2. I/O loop
|
||||
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/docs/async-rust-tokio-io/3-tokio-io-patterns/" class="">
|
||||
|
||||
3. Tokio I/O patterns
|
||||
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div style="margin-top: 30px; margin-bottom: 30px">
|
||||
<b>More recent series:</b>
|
||||
<ul>
|
||||
<li><a href="/rust-tokio-io/">1. Async Rust & Tokio I/O Streams: Backpressure, Concurrency, and Ergonomics <span style="padding:0 2px;border-radius:2px ;background-color:#e84118;color: aliceblue;">new</span></a></li><a href="/rust-tokio-io/">
|
||||
</a><li><a href="/rust-tokio-io/"></a><a href="/docs/resolver-dual-stack-application/0-sre-should-know-about-gnu-linux-resolvers-and-dual-stack-applications/">2. Resolvers and Dual-Stack applications</a></li><a href="/docs/resolver-dual-stack-application/0-sre-should-know-about-gnu-linux-resolvers-and-dual-stack-applications/">
|
||||
</a><li><a href="/docs/resolver-dual-stack-application/0-sre-should-know-about-gnu-linux-resolvers-and-dual-stack-applications/"></a><a href="/docs/page-cache/0-linux-page-cache-for-sre/">3. Linux Page Cache mini book</a></li><a href="/docs/page-cache/0-linux-page-cache-for-sre/">
|
||||
</a><li><a href="/docs/page-cache/0-linux-page-cache-for-sre/"></a><a href="/docs/fd-pipe-session-terminal/0-sre-should-know-about-gnu-linux-shell-related-internals-file-descriptors-pipes-terminals-user-sessions-process-groups-and-daemons">4. File descriptors, pipes, terminals, user sessions, process groups and daemons</a></li><a href="/docs/fd-pipe-session-terminal/0-sre-should-know-about-gnu-linux-shell-related-internals-file-descriptors-pipes-terminals-user-sessions-process-groups-and-daemons">
|
||||
</a></ul><a href="/docs/fd-pipe-session-terminal/0-sre-should-know-about-gnu-linux-shell-related-internals-file-descriptors-pipes-terminals-user-sessions-process-groups-and-daemons">
|
||||
</a></div><a href="/docs/fd-pipe-session-terminal/0-sre-should-know-about-gnu-linux-shell-related-internals-file-descriptors-pipes-terminals-user-sessions-process-groups-and-daemons">
|
||||
|
||||
</a><div style="margin-top: 30px; margin-bottom: 30px"><a href="/docs/fd-pipe-session-terminal/0-sre-should-know-about-gnu-linux-shell-related-internals-file-descriptors-pipes-terminals-user-sessions-process-groups-and-daemons">
|
||||
<b>Open Source Projects</b>
|
||||
</a><ul><a href="/docs/fd-pipe-session-terminal/0-sre-should-know-about-gnu-linux-shell-related-internals-file-descriptors-pipes-terminals-user-sessions-process-groups-and-daemons">
|
||||
</a><li><a href="/docs/fd-pipe-session-terminal/0-sre-should-know-about-gnu-linux-shell-related-internals-file-descriptors-pipes-terminals-user-sessions-process-groups-and-daemons">
|
||||
</a><a href="/posts/trixter-chaos-proxy/"> • trixter chaos proxy</a>
|
||||
</li>
|
||||
<li><a href="https://crates.io/crates/tokio-netem"> • tokio-netem</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<ul>
|
||||
|
||||
<li>
|
||||
<a href="https://twitter.com/brk0v/" target="_blank" rel="noopener"><i class="bi bi-twitter"></i>
|
||||
Twitter
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="https://www.linkedin.com/in/biriukov/" target="_blank" rel="noopener"><i class="bi bi-linkedin"></i>
|
||||
Linkedin
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="https://github.com/brk0v/" target="_blank" rel="noopener"><i class="bi bi-github"></i>
|
||||
Github
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div style="margin-top: 30px;">
|
||||
<p xmlns:cc="http://creativecommons.org/ns#">
|
||||
This content is licensed under
|
||||
<a href="http://creativecommons.org/licenses/by-nc/4.0/?ref=chooser-v1" target="_blank" rel="license noopener noreferrer" style="display:inline-block; ">CC BY-NC 4.0<img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1"><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1"><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/nc.svg?ref=chooser-v1">
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div class="book-page">
|
||||
<header class="book-header">
|
||||
|
||||
<div class="flex align-center justify-between">
|
||||
<label for="menu-control">
|
||||
<img src="/svg/menu.svg" class="book-icon" alt="Menu">
|
||||
</label>
|
||||
|
||||
<strong>Async Rust with Tokio I/O Streams: Backpressure, Concurrency, and Ergonomics</strong>
|
||||
|
||||
<label for="toc-control">
|
||||
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
|
||||
<article class="markdown book-article"><h1 id="async-rust-with-tokio-io-streams-backpressure-concurrency-and-ergonomics">
|
||||
Async Rust with Tokio I/O Streams: Backpressure, Concurrency, and Ergonomics
|
||||
<a class="anchor" href="#async-rust-with-tokio-io-streams-backpressure-concurrency-and-ergonomics">#</a>
|
||||
</h1>
|
||||
<p class="updated-right">
|
||||
<i>
|
||||
<time datetime="2025-10">Last updated: Oct 2025</time>
|
||||
</i>
|
||||
</p>
|
||||
<p><strong>Contents</strong></p>
|
||||
<ul>
|
||||
<li><a href="/rust-tokio-io/">1 Async Rust with Tokio IO Streams: Backpressure, Concurrency, and Ergonomics</a>
|
||||
<ul>
|
||||
<li><a href="/docs/async-rust-tokio-io/1-async-rust-with-tokio-io-streams-backpressure-concurrency-and-ergonomics/#backpressure">Backpressure</a></li>
|
||||
<li><a href="/docs/async-rust-tokio-io/1-async-rust-with-tokio-io-streams-backpressure-concurrency-and-ergonomics/#cancellation">Cancellation</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="/docs/async-rust-tokio-io/2-io-loop/">2. I/O loop</a>
|
||||
<ul>
|
||||
<li><a href="/docs/async-rust-tokio-io/2-io-loop/#backpressure-propagation">Backpressure propagation</a></li>
|
||||
<li><a href="/docs/async-rust-tokio-io/2-io-loop/#concurrency">Concurrency</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="/docs/async-rust-tokio-io/3-tokio-io-patterns/">3. Tokio I/O Patterns</a>
|
||||
<ul>
|
||||
<li><a href="/docs/async-rust-tokio-io/3-tokio-io-patterns/#tcp-split-stream">TCP split stream</a></li>
|
||||
<li><a href="/docs/async-rust-tokio-io/3-tokio-io-patterns/#split-generic-asyncreadasyncwrite-stream">Split generic <code>AsyncRead+AsyncWrite</code> stream</a></li>
|
||||
<li><a href="/docs/async-rust-tokio-io/3-tokio-io-patterns/#bidirectional-driver-for-io-without-split">Bidirectional driver for I/O without split</a></li>
|
||||
<li><a href="/docs/async-rust-tokio-io/3-tokio-io-patterns/#framed-io">Framed I/O</a></li>
|
||||
<li><a href="/docs/async-rust-tokio-io/3-tokio-io-patterns/#35-bidirectional-driver-for-framed-io">Bidirectional driver for framed I/O</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p>There are many excellent, straightforward guides for getting started with Async Rust and Tokio. Most focus on the core building blocks: Tokio primitives, Rust futures, and concepts such as <code>Pin</code>/<code>Unpin</code>, and often finish with a simple TCP client-server sample. In fact, the typical tutorial example can usually be reduced to something as simple as:</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#66d9ef">loop</span> {
|
||||
</span></span><span style="display:flex;"><span> tokio::<span style="color:#a6e22e">select!</span> {
|
||||
</span></span><span style="display:flex;"><span> Some(msg) <span style="color:#f92672">=</span> rx.recv() <span style="color:#f92672">=></span> {
|
||||
</span></span><span style="display:flex;"><span> stream.write_all(<span style="color:#f92672">&</span>msg).<span style="color:#66d9ef">await</span><span style="color:#f92672">?</span>;
|
||||
</span></span><span style="display:flex;"><span> stream.flush().<span style="color:#66d9ef">await</span><span style="color:#f92672">?</span>;
|
||||
</span></span><span style="display:flex;"><span> }
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span> res <span style="color:#f92672">=</span> stream.read(<span style="color:#f92672">&</span><span style="color:#66d9ef">mut</span> read_buf) <span style="color:#f92672">=></span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">let</span> n <span style="color:#f92672">=</span> res<span style="color:#f92672">?</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> n <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// EOF - server closed connection
|
||||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">eprintln!</span>(<span style="color:#e6db74">"Server closed the connection."</span>);
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">break</span>; <span style="color:#75715e">//exit
|
||||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> }
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">print!</span>(<span style="color:#e6db74">"</span><span style="color:#e6db74">{}</span><span style="color:#e6db74">"</span>, String::from_utf8_lossy(<span style="color:#f92672">&</span>read_buf[<span style="color:#f92672">..</span>n]));
|
||||
</span></span><span style="display:flex;"><span> }
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span> <span style="color:#f92672">=></span> <span style="color:#66d9ef">break</span>
|
||||
</span></span><span style="display:flex;"><span> }
|
||||
</span></span><span style="display:flex;"><span>}
|
||||
</span></span></code><button type="button" class="copy-button" aria-label="Copy code to clipboard" title="Copy">Copy</button></pre></div><p>The code above is <strong>easy to read, straightforward, and idiomatic</strong>.</p>
|
||||
<p>It uses a Tokio <a href="https://docs.rs/tokio/latest/tokio/sync/mpsc/index.html" target="_blank" rel="noopener"><code>mpsc</code></a> receiver <code>rx</code> whose incoming messages are written to the I/O stream (the remote peer in TCP terms).
|
||||
On the other branch of the <code>select!</code>, it reads data into <code>read_buf</code> until reaching the end of file (EOF).
|
||||
I’m sure you’ve seen many examples that follow exactly this pattern.</p>
|
||||
<p>But if we think not only about the functionality of the above code, but also about the mechanics and design behind it, some crucial details emerge.
|
||||
With the loop-select pattern we have concurrent reads and writes.
|
||||
This means the Tokio runtime scheduler multiplexes them and runs <strong>sequentially</strong> one after another, and never truly in parallel on two OS threads or CPU cores (assuming the default multithreaded runtime with <code>#[tokio::main]</code>).
|
||||
All branches of the <code>select!</code> are awaited for readiness and then run one by one in a pseudo-random order, unless the <code>biased;</code> argument is set, in which case they run in the provided order.
|
||||
In our example, we have two branches:</p>
|
||||
<ul>
|
||||
<li>read from <code>rx</code> channel;</li>
|
||||
<li>read from the I/O stream.</li>
|
||||
</ul>
|
||||
<p>Async Rust implicitly generates a state machine to make the polling of futures easier. This allows <code>select!</code> to run its branches concurrently. But when we fall inside the first branch and explicitly await inside it, we ask Rust to move the task’s state machine into a state where the only possible wake signal for the task is a wake notification from the <code>write_all()</code> future (or <code>flush()</code> later). The code logic doesn’t allow us to await on <code>write_all()</code>/<code>flush()</code> and <code>read()</code> (from the second branch of the <code>select!</code>) at the same time .</p>
|
||||
<p>Code written in this way naturally introduces <strong>backpressure</strong>. This side effect deserves careful attention and a clear understanding.</p>
|
||||
<h2 id="backpressure">
|
||||
Backpressure
|
||||
<a class="anchor" href="#backpressure">#</a>
|
||||
</h2>
|
||||
<img style="float:right;" alt="This is fine" src="../images/ok.png" width="40%" class="img-center">
|
||||
<p>If a <code>write()</code> or <code>flush()</code> call becomes blocked for any reason, the entire loop-select is effectively blocked as well, meaning no reads will occur.
|
||||
This can lead to <strong>read starvation</strong> and, if the remote peer continues writing, to an inflated receive socket buffer and eventual <strong>backpressure</strong> on the remote peer side.</p>
|
||||
<p>Such backpressure can actually be useful when the code should avoid reading more data if it can’t send a timely response.
|
||||
In client-server communication, this makes sense: your server might not want to start processing work, or buffer it in memory, if the client isn’t ready to read the response to its request.</p>
|
||||
<p>But as usual, there’s no one-size-fits-all solution, it isn’t always the desired behavior. For example, if your code acts as a proxy or bridge, you’ll likely still want to continue draining the reader buffer, because, for example, the decision logic lives outside your module.</p>
|
||||
<p>Another use case is when <strong>producing data doesn’t fully depend on what you read</strong>.
|
||||
This often applies on the client side: a client may want to receive the response to a previously sent request as quickly as possible while concurrently sending a new, unrelated one.</p>
|
||||
<p>More examples are protocols where <strong>reads unblock writes</strong>, where reads are crucial and should always be handled. For instance, TCP itself is such a protocol. TCP flow control sends window size updates with ACKs to notify the remote peer to continue sending and/or increase the amount of in-flight data. HTTP/2 also has a <code>WINDOW_UPDATE</code> frame used for the same purpose – the consumer controls the producer’s pace.</p>
|
||||
<h3 id="experiment">
|
||||
Experiment
|
||||
<a class="anchor" href="#experiment">#</a>
|
||||
</h3>
|
||||
<p>Let’s run a small experiment to reproduce TCP backpressure and explore a few related GNU/Linux tools.</p>
|
||||
<p>We can write a TCP server that intentionally blocks its read calls. Also in order to trigger backpressure earlier, server sets its socket receive buffer to the minimum allowed value with <code>setsockopt</code> and <code>SO_RCVBUF</code> (the OS rounds it up to a small default value):</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#f92672">..</span>.
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> s2 <span style="color:#f92672">=</span> Socket::from(std_stream);
|
||||
</span></span><span style="display:flex;"><span>s2.set_recv_buffer_size(<span style="color:#ae81ff">1</span>)<span style="color:#f92672">?</span>;
|
||||
</span></span><span style="display:flex;"><span><span style="color:#f92672">..</span>.
|
||||
</span></span></code><button type="button" class="copy-button" aria-label="Copy code to clipboard" title="Copy">Copy</button></pre></div><p>If server doesn’t read data from buffer, its receive buffer fills up and pushes back on the TCP sender.
|
||||
This effectively simulates slow request processing, for example, when the next request hits a slow database while the previous response is still being flushed to the socket to deliver to the client.</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#66d9ef">async</span> <span style="color:#66d9ef">fn</span> <span style="color:#a6e22e">handle_client</span>(<span style="color:#66d9ef">mut</span> stream: <span style="color:#a6e22e">TcpStream</span>) -> <span style="color:#a6e22e">std</span>::io::Result<span style="color:#f92672"><</span>()<span style="color:#f92672">></span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">println!</span>(<span style="color:#e6db74">"start serving"</span>);
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">let</span> <span style="color:#66d9ef">mut</span> i <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">loop</span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Don't read any data to emulate backpressure.
|
||||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// let mut buf = [0u8; 1024];
|
||||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// let n = stream.read(&mut buf).await?;
|
||||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// if n == 0 {
|
||||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// break;
|
||||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// }
|
||||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// println!("Received {n} bytes");
|
||||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
|
||||
</span></span><span style="display:flex;"><span> i <span style="color:#f92672">+=</span> <span style="color:#ae81ff">1</span>;
|
||||
</span></span><span style="display:flex;"><span> sleep(Duration::from_millis(<span style="color:#ae81ff">10</span>)).<span style="color:#66d9ef">await</span>;
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span> stream
|
||||
</span></span><span style="display:flex;"><span> .write_all(<span style="color:#a6e22e">format!</span>(<span style="color:#e6db74">"message from server: </span><span style="color:#e6db74">{i}</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">"</span>).as_bytes())
|
||||
</span></span><span style="display:flex;"><span> .<span style="color:#66d9ef">await</span><span style="color:#f92672">?</span>;
|
||||
</span></span><span style="display:flex;"><span> stream.flush().<span style="color:#66d9ef">await</span><span style="color:#f92672">?</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">println!</span>(<span style="color:#e6db74">"written: </span><span style="color:#e6db74">{i}</span><span style="color:#e6db74">"</span>);
|
||||
</span></span><span style="display:flex;"><span> }
|
||||
</span></span><span style="display:flex;"><span>}
|
||||
</span></span></code><button type="button" class="copy-button" aria-label="Copy code to clipboard" title="Copy">Copy</button></pre></div><p>The full code can be found on <a href="https://github.com/brk0v/blog-async-rust-tokio-io/tree/main/src" target="_blank" rel="noopener">github</a>.</p>
|
||||
<p>And run it:</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ cargo run --bin 1_server_recvbuf_set
|
||||
</span></span></code><button type="button" class="copy-button" aria-label="Copy code to clipboard" title="Copy">Copy</button></pre></div><p>In the other console window run the client:</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ cargo run --bin 1_client_simple
|
||||
</span></span></code><button type="button" class="copy-button" aria-label="Copy code to clipboard" title="Copy">Copy</button></pre></div><p>As you can see from the output logs, the client stops reading from the server even while the server keeps writing. Packets pile up in the client’s receive buffer, eventually filling it and forcing TCP to apply backpressure in the opposite direction.</p>
|
||||
<p>In the <code>tcpdump</code> output you can see the server on port 8080 sending a zero-window (win 0) ACKs, notifying the client that its receive buffer is full.</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ sudo tcpdump -i any -n -s0 port <span style="color:#ae81ff">8080</span>
|
||||
</span></span><span style="display:flex;"><span>...
|
||||
</span></span><span style="display:flex;"><span>21:36:00.095027 lo In IP 127.0.0.1.8080 > 127.0.0.1.58090: Flags <span style="color:#f92672">[</span>P.<span style="color:#f92672">]</span>, seq 5668:5693, ack 102401, win 0, options <span style="color:#f92672">[</span>nop,nop,TS val <span style="color:#ae81ff">2038288450</span> ecr 2038288438<span style="color:#f92672">]</span>, length 25: HTTP
|
||||
</span></span><span style="display:flex;"><span>...
|
||||
</span></span></code><button type="button" class="copy-button" aria-label="Copy code to clipboard" title="Copy">Copy</button></pre></div><p>The <code>ss</code> can help us too to find the stalled send buffer:</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ ss -tan | grep 127.0.0.1:58090
|
||||
</span></span><span style="display:flex;"><span>State Recv-Q Send-Q Local Address:Port Peer Address:Port
|
||||
</span></span><span style="display:flex;"><span>ESTAB <span style="color:#ae81ff">102400</span> <span style="color:#ae81ff">0</span> 127.0.0.1:8080 127.0.0.1:58090
|
||||
</span></span><span style="display:flex;"><span>ESTAB <span style="color:#ae81ff">869937</span> <span style="color:#ae81ff">2628271</span> 127.0.0.1:58090 127.0.0.1:8080
|
||||
</span></span></code><button type="button" class="copy-button" aria-label="Copy code to clipboard" title="Copy">Copy</button></pre></div><p>In simplified terms, the TCP connection behaves like a network of queues. Congestion control and flow control cooperate to size those queues, balance throughput, and protect each endpoint from overload.</p>
|
||||
<img style="margin: 50px 0 20px 0" alt="TCP buffers" src="../images/tcp-buffers.png" width="100%" class="img-center">
|
||||
<div class="text-center">
|
||||
Figure 1. – TCP buffers for simplex communication
|
||||
</div>
|
||||
<p>Usually write calls to a socket complete almost instantly (0.5-5 µs depending on size) because they land in memory (userspace buffers such as <code><a href="https://docs.rs/tokio/latest/tokio/io/struct.BufWriter.html" target="_blank" rel="noopener">BufWriter</a></code> or the kernel’s TCP send buffer) without waiting for remote acknowledgements.</p>
|
||||
<p>This asynchronous nature of writes can hide subtle problems: <strong>background write failures, timeouts, and uncertainty of delivery often surface only under congestion</strong>. In our toy setup, <strong>small writes and generous buffers mask pending backpressure</strong>.</p>
|
||||
<blockquote class="book-hint info">
|
||||
<p><strong>Note:</strong></p>
|
||||
<p>A useful per-socket/system-wide setting to improve responsiveness and backpressure behavior is <code><a href="https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt" target="_blank" rel="noopener">TCP_NOTSENT_LOWAT</a></code>. Cloudflare has a good <a href="https://blog.cloudflare.com/http-2-prioritization-with-nginx/" target="_blank" rel="noopener">write-up</a>.</p>
|
||||
|
||||
</blockquote>
|
||||
|
||||
<h2 id="cancellation">
|
||||
Cancellation
|
||||
<a class="anchor" href="#cancellation">#</a>
|
||||
</h2>
|
||||
<p>Another interesting part of the code is <strong>how to perform cancellation while backpressure is applied</strong>. We might need to stop processing for many reasons: restart, upstream/downstream abort, timeouts, or simply because the caller no longer needs the result.</p>
|
||||
<p>In the code above, simply adding a cancellation branch to <code>select!</code> will not work if the task is blocked on <code>write_all()</code>:</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> cancel <span style="color:#f92672">=</span> CancellationToken::new();
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">loop</span> {
|
||||
</span></span><span style="display:flex;"><span> tokio::<span style="color:#a6e22e">select!</span> {
|
||||
</span></span><span style="display:flex;"><span> Some(msg) <span style="color:#f92672">=</span> rx.recv() <span style="color:#f92672">=></span> {
|
||||
</span></span><span style="display:flex;"><span> stream.write_all(<span style="color:#f92672">&</span>msg).<span style="color:#66d9ef">await</span><span style="color:#f92672">?</span>;
|
||||
</span></span><span style="display:flex;"><span> stream.flush().<span style="color:#66d9ef">await</span><span style="color:#f92672">?</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">println!</span>(<span style="color:#e6db74">"client's written"</span>);
|
||||
</span></span><span style="display:flex;"><span> }
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span> res <span style="color:#f92672">=</span> stream.read(<span style="color:#f92672">&</span><span style="color:#66d9ef">mut</span> read_buf) <span style="color:#f92672">=></span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">let</span> n <span style="color:#f92672">=</span> res<span style="color:#f92672">?</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> n <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// EOF - server closed connection
|
||||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">eprintln!</span>(<span style="color:#e6db74">"Server closed the connection."</span>);
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">break</span>; <span style="color:#75715e">//exit
|
||||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> }
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">print!</span>(<span style="color:#e6db74">"</span><span style="color:#e6db74">{}</span><span style="color:#e6db74">"</span>, String::from_utf8_lossy(<span style="color:#f92672">&</span>read_buf[<span style="color:#f92672">..</span>n]));
|
||||
</span></span><span style="display:flex;"><span> }
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span> _ <span style="color:#f92672">=</span> cancel.cancelled() <span style="color:#f92672">=></span> <span style="color:#66d9ef">break</span>, <span style="color:#75715e">// <---------------- cancellation
|
||||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span> <span style="color:#f92672">=></span> <span style="color:#66d9ef">break</span>
|
||||
</span></span><span style="display:flex;"><span> }
|
||||
</span></span><span style="display:flex;"><span>}
|
||||
</span></span></code><button type="button" class="copy-button" aria-label="Copy code to clipboard" title="Copy">Copy</button></pre></div><p>At that point the state machine awaits only <code>write_all()</code>, remaining stuck in <code>Poll::Pending</code> inside the first branch.</p>
|
||||
<p>The first idea is to apply the cancellation token through every <code>await</code> calls:</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> cancel <span style="color:#f92672">=</span> CancellationToken::new();
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">loop</span> {
|
||||
</span></span><span style="display:flex;"><span> tokio::<span style="color:#a6e22e">select!</span> {
|
||||
</span></span><span style="display:flex;"><span> Some(msg) <span style="color:#f92672">=</span> rx.recv() <span style="color:#f92672">=></span> {
|
||||
</span></span><span style="display:flex;"><span> tokio::<span style="color:#a6e22e">select!</span> {
|
||||
</span></span><span style="display:flex;"><span> res <span style="color:#f92672">=</span> stream.write_all(<span style="color:#f92672">&</span>msg) <span style="color:#f92672">=></span> res,
|
||||
</span></span><span style="display:flex;"><span> _ <span style="color:#f92672">=</span> cancel.cancelled() <span style="color:#f92672">=></span> <span style="color:#66d9ef">break</span>
|
||||
</span></span><span style="display:flex;"><span> }<span style="color:#f92672">?</span>;
|
||||
</span></span><span style="display:flex;"><span> tokio::<span style="color:#a6e22e">select!</span> {
|
||||
</span></span><span style="display:flex;"><span> res <span style="color:#f92672">=</span> stream.flush() <span style="color:#f92672">=></span> res,
|
||||
</span></span><span style="display:flex;"><span> _ <span style="color:#f92672">=</span> cancel.cancelled() <span style="color:#f92672">=></span> <span style="color:#66d9ef">break</span>
|
||||
</span></span><span style="display:flex;"><span> }<span style="color:#f92672">?</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">println!</span>(<span style="color:#e6db74">"client's written"</span>);
|
||||
</span></span><span style="display:flex;"><span> }
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span> res <span style="color:#f92672">=</span> stream.read(<span style="color:#f92672">&</span><span style="color:#66d9ef">mut</span> read_buf) <span style="color:#f92672">=></span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">let</span> n <span style="color:#f92672">=</span> res<span style="color:#f92672">?</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> n <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// EOF - server closed connection
|
||||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">eprintln!</span>(<span style="color:#e6db74">"Server closed the connection."</span>);
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">break</span>; <span style="color:#75715e">//exit
|
||||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> }
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">print!</span>(<span style="color:#e6db74">"</span><span style="color:#e6db74">{}</span><span style="color:#e6db74">"</span>, String::from_utf8_lossy(<span style="color:#f92672">&</span>read_buf[<span style="color:#f92672">..</span>n]));
|
||||
</span></span><span style="display:flex;"><span> }
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span> _ <span style="color:#f92672">=</span> cancel.cancelled() <span style="color:#f92672">=></span> <span style="color:#66d9ef">break</span>,
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span> <span style="color:#f92672">=></span> <span style="color:#66d9ef">break</span>
|
||||
</span></span><span style="display:flex;"><span> }
|
||||
</span></span><span style="display:flex;"><span>}
|
||||
</span></span></code><button type="button" class="copy-button" aria-label="Copy code to clipboard" title="Copy">Copy</button></pre></div><p>This works, but it scales poorly: every new await point needs another nested <code>select!</code>, and readability drops fast.</p>
|
||||
<p>Later in the post I’ll show a tidier cancellation pattern, especially if you need a simple “short-circuit” solution.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/docs/async-rust-tokio-io/2-io-loop/" class="book-btn right-button">
|
||||
Read next chapter →
|
||||
</a>
|
||||
|
||||
</article>
|
||||
|
||||
|
||||
|
||||
<footer class="book-footer">
|
||||
|
||||
<div class="flex flex-wrap justify-between">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script defer="" src="/my_js/copy-code.js"></script>
|
||||
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
<div class="book-comments">
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<label for="menu-control" class="hidden book-menu-overlay"></label>
|
||||
</div>
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
|
||||
<div class="cookie-container active">
|
||||
<p>
|
||||
This website uses "<b>cookies</b>".
|
||||
Using this website means you're OK with this.
|
||||
If you are <b>NOT</b>, please close the site page.
|
||||
</p>
|
||||
<button class="cookie-btn">
|
||||
ACCEPT AND CLOSE
|
||||
</button>
|
||||
</div>
|
||||
<script src="/my_js/cookie.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body></html>
|
||||
25
tmp/changelog_search.html
Normal file
25
tmp/changelog_search.html
Normal file
File diff suppressed because one or more lines are too long
121
tmp/claude_code_docs.html
Normal file
121
tmp/claude_code_docs.html
Normal file
File diff suppressed because one or more lines are too long
9857
tmp/claude_code_product.html
Normal file
9857
tmp/claude_code_product.html
Normal file
File diff suppressed because one or more lines are too long
25
tmp/duckduckgo_reqwest.html
Normal file
25
tmp/duckduckgo_reqwest.html
Normal file
File diff suppressed because one or more lines are too long
25
tmp/duckduckgo_results.html
Normal file
25
tmp/duckduckgo_results.html
Normal file
File diff suppressed because one or more lines are too long
1923
tmp/github_claude_code.html
Normal file
1923
tmp/github_claude_code.html
Normal file
File diff suppressed because one or more lines are too long
4964
tmp/introl_guide.html
Normal file
4964
tmp/introl_guide.html
Normal file
File diff suppressed because it is too large
Load Diff
41
tmp/npm_claude_code.html
Normal file
41
tmp/npm_claude_code.html
Normal file
File diff suppressed because one or more lines are too long
129
tmp/poespas_streaming.html
Normal file
129
tmp/poespas_streaming.html
Normal file
@@ -0,0 +1,129 @@
|
||||
<html lang="en-GB"><head>
|
||||
<meta charset="utf-8">
|
||||
<title>Asynchronous Streaming in Rust with Tokio: A Deep Dive into Efficient Concurrency | Poespas Blog</title>
|
||||
<meta name="description" content="Introduction Rust has emerged as a popular choice for building high-performance, concurrent systems due to its ownership model, memory safety features, and">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="generator" content="Eleventy v2.0.0" built="2025-03-09T12:43:27.269Z">
|
||||
|
||||
<meta property="og:title" content="Asynchronous Streaming in Rust with Tokio: A Deep Dive into Efficient Concurrency | Poespas Blog">
|
||||
<meta property="og:description" content="Introduction Rust has emerged as a popular choice for building high-performance, concurrent systems due to its ownership model, memory safety features, and">
|
||||
<meta property="og:url" content="https://blog.poespas.me">
|
||||
<meta property="og:site_name" content="Poespas Blog">
|
||||
<meta property="og:type" content="article">
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Asynchronous Streaming in Rust with Tokio: A Deep Dive into Efficient Concurrency | Poespas Blog">
|
||||
<meta name="twitter:description" content="Introduction Rust has emerged as a popular choice for building high-performance, concurrent systems due to its ownership model, memory safety features, and">
|
||||
|
||||
<link rel="shortcut icon" href="/images/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="apple-touch-icon" href="/images/apple-touch-icon.png">
|
||||
|
||||
<link rel="stylesheet" href="/css/styles.min.css"> <link rel="alternate" type="application/rss+xml" title="RSS" href="/feeds.xml">
|
||||
<script defer="" src="https://umami.mageberg.com/script.js" data-website-id="7a091986-a5ec-4b97-804e-94bf02703165"></script>
|
||||
</head>
|
||||
<body class="theme-base-0b">
|
||||
|
||||
<div role="region" class="sidebar">
|
||||
<div class="container sidebar-sticky">
|
||||
<div class="sidebar-about">
|
||||
<h1><a href="/">Poespas Blog</a></h1>
|
||||
<p class="lead">Every day smarter!</p>
|
||||
</div>
|
||||
|
||||
<nav class="sidebar-nav">
|
||||
<a class="sidebar-nav-item" href="/">Home</a>
|
||||
<a class="sidebar-nav-item" href="/about/">About</a>
|
||||
<a class="sidebar-nav-item" href="/sitemap">Sitemap</a>
|
||||
</nav>
|
||||
|
||||
<p>© 2025 All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div role="main" class="content container">
|
||||
<article class="post" data-input-path="/posts/2024/08/08/rust-async-streaming-with-tokio">
|
||||
<h2 class="post-title">Asynchronous Streaming in Rust with Tokio: A Deep Dive into Efficient Concurrency</h2>
|
||||
<time class="post-date" datetime="2024-08-08T10:22:27.000Z">8 August 2024</time>
|
||||
<h2>Introduction</h2>
|
||||
<p>Rust has emerged as a popular choice for building high-performance, concurrent systems due to its ownership model, memory safety features, and native support for concurrency. When it comes to handling large datasets or streaming data from various sources, asynchronous programming becomes essential. In this article, we’ll delve into the world of asynchronous streaming in Rust using the Tokio framework.</p>
|
||||
<h2>What is Asynchronous Streaming?</h2>
|
||||
<p>Asynchronous streaming allows your program to process a stream of data without blocking on individual elements. This approach is particularly useful when dealing with large datasets or real-time data sources. By using asynchronous programming, you can efficiently handle multiple streams concurrently, improving overall system performance and responsiveness.</p>
|
||||
<h2>Tokio’s Async Streaming API</h2>
|
||||
<p>Tokio provides a robust async streaming API that makes it easy to implement efficient concurrency in your Rust applications. The <code>tokio::sync::mpsc</code> (multi-producer, single-consumer) channel is a key component of this API, enabling you to send and receive data asynchronously.</p>
|
||||
<h3>Code Example: Basic Asynchronous Streaming with Tokio</h3>
|
||||
<pre class="hljs"><code><span class="hljs-keyword">use</span> tokio::sync::mpsc;
|
||||
<span class="hljs-keyword">use</span> tokio::time::{interval, Interval};
|
||||
<span class="hljs-keyword">async</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">producer</span>(<span class="hljs-keyword">mut</span> sender: mpsc::Sender<<span class="hljs-type">i32</span>>, interval: Interval) {
|
||||
<span class="hljs-keyword">for</span> <span class="hljs-variable">i</span> <span class="hljs-keyword">in</span> <span class="hljs-number">0</span>..<span class="hljs-number">10</span> {
|
||||
sender.<span class="hljs-title function_ invoke__">send</span>(i).<span class="hljs-keyword">await</span>;
|
||||
interval.<span class="hljs-title function_ invoke__">sleep</span>().<span class="hljs-keyword">await</span>;
|
||||
}
|
||||
}
|
||||
<span class="hljs-meta">#[tokio::main]</span>
|
||||
<span class="hljs-keyword">async</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">main</span>() <span class="hljs-punctuation">-></span> <span class="hljs-type">Result</span><(), <span class="hljs-type">Box</span><<span class="hljs-keyword">dyn</span> std::error::Error>> {
|
||||
<span class="hljs-keyword">let</span> (sender, receiver) = mpsc::<span class="hljs-title function_ invoke__">channel</span>(<span class="hljs-number">32</span>);
|
||||
<span class="hljs-keyword">let</span> <span class="hljs-variable">producer_handle</span> = tokio::<span class="hljs-title function_ invoke__">spawn</span>(<span class="hljs-title function_ invoke__">producer</span>(sender, <span class="hljs-title function_ invoke__">interval</span>()));
|
||||
<span class="hljs-keyword">while</span> <span class="hljs-keyword">let</span> <span class="hljs-variable">Some</span>(i) = receiver.<span class="hljs-title function_ invoke__">recv</span>().<span class="hljs-keyword">await</span> {
|
||||
<span class="hljs-built_in">println!</span>(<span class="hljs-string">"Received: {}"</span>, i);
|
||||
}
|
||||
producer_handle.<span class="hljs-keyword">await</span>?;
|
||||
<span class="hljs-title function_ invoke__">Ok</span>(())
|
||||
}
|
||||
</code></pre>
|
||||
<p>In this example, we create a Tokio async channel and spawn a producer task that sends numbers from 0 to 9 using the <code>sender</code> channel. The main task receives these numbers using the <code>receiver</code> channel.</p>
|
||||
<h2>Conclusion</h2>
|
||||
<p>Asynchronous streaming is a powerful technique in Rust programming that enables efficient concurrency and scalability. By leveraging Tokio’s async streaming API, you can build high-performance systems that efficiently handle large datasets or real-time data sources. This article has provided a basic example of asynchronous streaming with Tokio, and you can further explore the possibilities by experimenting with different use cases and configurations.</p>
|
||||
</article>
|
||||
|
||||
<div class="related">
|
||||
<h2>Related Posts</h2>
|
||||
<ul class="related-posts"> <li>
|
||||
<a href="/posts/2025/03/05/expressjs-error-handling-production-deployments/">
|
||||
<h3>
|
||||
Advanced Express.js Error Handling Strategies for Production Deployments <br>
|
||||
</h3>
|
||||
</a>
|
||||
<p class="post-description">Optimize error handling with advanced Express.js techniques, ensuring a secure and reliable production environment.</p><p>
|
||||
<small>5 March 2025</small>
|
||||
</p></li> <li>
|
||||
<a href="/posts/2024/08/13/thales-group-secure-boot-on-linux/">
|
||||
<h3>
|
||||
Secure Boot on Linux with Thales Group: Ensuring Trust in the Boot Process <br>
|
||||
</h3>
|
||||
</a>
|
||||
<p class="post-description">Learn how Thales Group's secure boot solution works on Linux systems, ensuring secure and trusted boot processes.</p><p>
|
||||
<small>13 August 2024</small>
|
||||
</p></li> <li>
|
||||
<a href="/posts/2024/08/21/objective-c-dealing-with-large-image-data-in-ui-table-view/">
|
||||
<h3>
|
||||
Displaying Large Images in a UI Table View with Objective-C: A Practical Approach <br>
|
||||
</h3>
|
||||
</a>
|
||||
<p class="post-description">Learn how to efficiently display large images in a UI Table View using Objective-C.</p><p>
|
||||
<small>20 August 2024</small>
|
||||
</p></li> <li>
|
||||
<a href="/posts/2025/02/13/efficient-time-series-data-processing-apache-arrow-numpy/">
|
||||
<h3>
|
||||
Efficiently Handling Large-Scale Time Series Data with Apache Arrow and NumPy <br>
|
||||
</h3>
|
||||
</a>
|
||||
<p class="post-description">Learn how to optimize time series data processing using Apache Arrow and NumPy for faster analysis and better performance.</p><p>
|
||||
<small>13 February 2025</small>
|
||||
</p></li> <li>
|
||||
<a href="/posts/2024/08/14/imperva-ddos-defense-with-cdn-and-waf/">
|
||||
<h3>
|
||||
Protecting Your Website from DDoS Attacks with Imperva's CDN and WAF <br>
|
||||
</h3>
|
||||
</a>
|
||||
<p class="post-description">Learn how Imperva's CDN and WAF can help defend against DDoS attacks, protecting your website from malicious traffic.</p><p>
|
||||
<small>14 August 2024</small>
|
||||
</p></li> </ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- <script src="https://kulroakonsu.net/88/tag.min.js" data-zone="133071" async data-cfasync="false"></script> -->
|
||||
<script defer="" src="https://static.cloudflareinsights.com/beacon.min.js/vcd15cbe7772f49c399c6a5babf22c1241717689176015" integrity="sha512-ZpsOmlRQV6y907TI0dKBHq9Md29nnaEIPlkf84rnaERnq6zvWvPUqr2ft8M1aS28oN72PdrCzSjY4U6VaAw1EQ==" data-cf-beacon="{"version":"2024.11.0","token":"89ea6fc96f81400e9ac57b5b53f5473c","r":1,"server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}" crossorigin="anonymous"></script>
|
||||
|
||||
|
||||
</body></html>
|
||||
380
tmp/rust_homepage.html
Normal file
380
tmp/rust_homepage.html
Normal file
@@ -0,0 +1,380 @@
|
||||
<html lang="en-US"><head>
|
||||
<meta charset="utf-8">
|
||||
<title>
|
||||
Rust Programming Language
|
||||
</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<meta name="description" content="A language empowering everyone to build reliable and efficient software.">
|
||||
|
||||
<!-- Twitter card -->
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:site" content="@rustlang">
|
||||
<meta name="twitter:creator" content="@rustlang">
|
||||
<meta name="twitter:title" content="">
|
||||
<meta name="twitter:description" content="A language empowering everyone to build reliable and efficient software.">
|
||||
<meta name="twitter:image" content="https://www.rust-lang.org/static/images/rust-social.jpg">
|
||||
|
||||
<!-- Facebook OpenGraph -->
|
||||
<meta property="og:title" content="">
|
||||
<meta property="og:description" content="A language empowering everyone to build reliable and efficient software.">
|
||||
<meta property="og:image" content="https://www.rust-lang.org/static/images/rust-social-wide.jpg">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:locale" content="en_US">
|
||||
|
||||
<!-- styles -->
|
||||
<link rel="stylesheet" href="/static/styles/a11y-dark.css">
|
||||
<link rel="stylesheet" href="/static/styles/vendor_10880690442070639967.css">
|
||||
<link rel="stylesheet" href="/static/styles/fonts_8049871103083011125.css">
|
||||
<link rel="stylesheet" href="/static/styles/app_2753916734537011643.css">
|
||||
|
||||
<!-- favicon -->
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/static/images/apple-touch-icon.png?v=ngJW8jGAmR">
|
||||
<link rel="icon" sizes="16x16" type="image/png" href="/static/images/favicon-16x16.png">
|
||||
<link rel="icon" sizes="32x32" type="image/png" href="/static/images/favicon-32x32.png">
|
||||
<link rel="icon" type="image/svg+xml" href="/static/images/favicon.svg">
|
||||
<link rel="manifest" href="/static/images/site.webmanifest?v=ngJW8jGAmR">
|
||||
<link rel="mask-icon" href="/static/images/safari-pinned-tab.svg?v=ngJW8jGAmR" color="#000000">
|
||||
<meta name="msapplication-TileColor" content="#ffffff">
|
||||
<meta name="msapplication-config" content="/static/images/browserconfig.xml?v=ngJW8jGAmR">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<!-- locales -->
|
||||
<link rel="alternate" href="https://www.rust-lang.org/en-US" hreflang="en-US">
|
||||
<link rel="alternate" href="https://www.rust-lang.org/es" hreflang="es">
|
||||
<link rel="alternate" href="https://www.rust-lang.org/fr" hreflang="fr">
|
||||
<link rel="alternate" href="https://www.rust-lang.org/it" hreflang="it">
|
||||
<link rel="alternate" href="https://www.rust-lang.org/ja" hreflang="ja">
|
||||
<link rel="alternate" href="https://www.rust-lang.org/pt-BR" hreflang="pt-BR">
|
||||
<link rel="alternate" href="https://www.rust-lang.org/ru" hreflang="ru">
|
||||
<link rel="alternate" href="https://www.rust-lang.org/tr" hreflang="tr">
|
||||
<link rel="alternate" href="https://www.rust-lang.org/zh-CN" hreflang="zh-CN">
|
||||
<link rel="alternate" href="https://www.rust-lang.org/zh-TW" hreflang="zh-TW">
|
||||
<link rel="alternate" href="https://www.rust-lang.org/" hreflang="x-default">
|
||||
|
||||
<script type="text/javascript">
|
||||
window.RUST_BASE_URL = "";
|
||||
</script>
|
||||
|
||||
<!-- Custom Highlight pack with: Rust, Markdown, TOML, Bash, JSON, YAML,
|
||||
and plaintext. -->
|
||||
<script src="/static/scripts/highlight.pack.js" defer=""></script>
|
||||
<script src="/static/scripts/init.js" defer=""></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="flex flex-row justify-center justify-end-l items-center flex-wrap ph2 pl3-ns pr3-ns pb3">
|
||||
<div class="brand flex-auto w-100 w-auto-l self-start tc tl-l">
|
||||
<a href="/" class="brand">
|
||||
<img class="v-mid ml0-l" alt="Rust Logo" src="/static/images/rust-logo-blk.svg">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ul class="nav list w-100 w-auto-l flex flex-none flex-row flex-wrap justify-center justify-end-l items-center pv2 ph0 ph4-ns">
|
||||
<li class="tc pv2 ph2 ph4-ns flex-20-s"><a href="/tools/install">Install</a></li>
|
||||
<li class="tc pv2 ph2 ph4-ns flex-20-s"><a href="/learn">Learn</a></li>
|
||||
<li class="tc pv2 ph2 ph4-ns flex-20-s"><a href="https://play.rust-lang.org/">Playground</a></li>
|
||||
<li class="tc pv2 ph2 ph4-ns flex-20-s"><a href="/tools">Tools</a></li>
|
||||
<li class="tc pv2 ph2 ph4-ns flex-20-s"><a href="/governance">Governance</a></li>
|
||||
<li class="tc pv2 ph2 ph4-ns flex-20-s"><a href="/community">Community</a></li>
|
||||
<li class="tc pv2 ph2 ph4-ns flex-20-s"><a href="https://blog.rust-lang.org/">Blog</a></li>
|
||||
</ul>
|
||||
|
||||
<div class=" w-100 w-auto-l flex-none flex justify-center pv4 pv-0-l languages">
|
||||
<div class="select">
|
||||
<label for="language-nav" class="hidden">Language</label>
|
||||
<select id="language-nav" data-current-lang="en-US">
|
||||
<option title="English (en-US)" value="en-US">English (en-US)</option>
|
||||
<option title="Español (es)" value="es">Español (es)</option>
|
||||
<option title="Français (fr)" value="fr">Français (fr)</option>
|
||||
<option title="Italiano (it)" value="it">Italiano (it)</option>
|
||||
<option title="日本語 (ja)" value="ja">日本語 (ja)</option>
|
||||
<option title="Português (pt-BR)" value="pt-BR">Português (pt-BR)</option>
|
||||
<option title="Русский (ru)" value="ru">Русский (ru)</option>
|
||||
<option title="Türkçe (tr)" value="tr">Türkçe (tr)</option>
|
||||
<option title="简体中文 (zh-CN)" value="zh-CN">简体中文 (zh-CN)</option>
|
||||
<option title="正體中文 (zh-TW)" value="zh-TW">正體中文 (zh-TW)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
<main><header class="mt3 mb6 w-100 mw-none ph3 mw8-m mw9-l center">
|
||||
<div class="flex flex-column flex-row-l">
|
||||
<div class="w-70-l mw8-l">
|
||||
<h1>Rust</h1>
|
||||
<h2 class="mt4 mb0 f2 f1-ns">
|
||||
A language empowering everyone <br class="dn db-ns"> to build reliable and efficient software.
|
||||
</h2>
|
||||
</div>
|
||||
<div class="w-30-l flex-column pl0-l pr0-l pl3 pr3">
|
||||
<a class="button button-download ph4 mt0 w-100" href="/learn/get-started">
|
||||
Get Started
|
||||
</a>
|
||||
<p class="tc f3 f2-l mt3">
|
||||
<a href="https://blog.rust-lang.org/releases/latest" class="download-link">Version 1.92.0</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section id="language-values" class="green">
|
||||
<div class="w-100 mw-none ph3 mw8-m mw9-l center f3">
|
||||
<header class="pb0">
|
||||
<h2>
|
||||
Why Rust?
|
||||
</h2>
|
||||
<div class="highlight"></div>
|
||||
</header>
|
||||
<div class="flex-none flex-l">
|
||||
<section class="w-100 pv2 pv0-l mt4">
|
||||
<h3 class="f2 f1-l">Performance</h3>
|
||||
<p class="f3 lh-copy">
|
||||
Rust is blazingly fast and memory-efficient: with no runtime or
|
||||
garbage collector, it can power performance-critical services, run on
|
||||
embedded devices, and easily integrate with other languages.
|
||||
</p>
|
||||
</section>
|
||||
<section class="w-100 pv2 pv0-l mt4 mh5-l">
|
||||
<h3 class="f2 f1-l">Reliability</h3>
|
||||
<p class="f3 lh-copy">
|
||||
Rust’s rich type system and ownership model guarantee memory-safety
|
||||
and thread-safety — enabling you to eliminate many classes of
|
||||
bugs at compile-time.
|
||||
</p>
|
||||
</section>
|
||||
<section class="w-100 pv2 pv0-l mt4">
|
||||
<h3 class="f2 f1-l">Productivity</h3>
|
||||
<p class="f3 lh-copy">
|
||||
Rust has great documentation, a friendly compiler with useful error
|
||||
messages, and top-notch tooling — an integrated package manager
|
||||
and build tool, smart multi-editor support with auto-completion and
|
||||
type inspections, an auto-formatter, and more.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="purple">
|
||||
<div class="w-100 mw-none ph3 mw8-m mw9-l center f3">
|
||||
<header>
|
||||
<h2>
|
||||
Build it in Rust
|
||||
</h2>
|
||||
<div class="highlight"></div>
|
||||
</header>
|
||||
|
||||
<div class="flex-none flex-l flex-row">
|
||||
<p class="flex-grow-1 pb2">
|
||||
In 2018, the Rust community decided to improve the programming experience
|
||||
for a few distinct domains (see <a href="https://blog.rust-lang.org/2018/03/12/roadmap.html">the 2018
|
||||
roadmap</a>). For these, you can find many high-quality crates and some
|
||||
awesome guides on how to get started.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex-none flex-l flex-row">
|
||||
<div class="flex flex-row flex-column-l justify-between-l mw8 measure-wide-l w-100 mt5 mt2-l">
|
||||
<div class="v-top tc-l">
|
||||
<img src="/static/images/cli.svg" alt="terminal" class="mw3 mw4-ns">
|
||||
</div>
|
||||
<div class="v-top pl4 pl0-l pt0 pt3-l measure-wide-l flex-l flex-column-l flex-auto-l justify-between-l">
|
||||
<h3 class="tc-l">
|
||||
Command Line
|
||||
</h3>
|
||||
<p class="flex-grow-1">
|
||||
Whip up a CLI tool quickly with Rust’s robust ecosystem.
|
||||
Rust helps you maintain your app with confidence and distribute it with ease.
|
||||
</p>
|
||||
<a href="/what/cli" class="button button-secondary">Building Tools</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row flex-column-l justify-between-l mw8 measure-wide-l w-100 mt5 mt2-l pl4-l">
|
||||
<div class="v-top tc-l">
|
||||
<img src="/static/images/webassembly.svg" alt="gear with puzzle piece elements" class="mw3 mw4-ns">
|
||||
</div>
|
||||
<div class="v-top pl4 pl0-l pt0 pt3-l measure-wide-l flex-l flex-column-l flex-auto-l justify-between-l">
|
||||
<h3 class="tc-l">
|
||||
WebAssembly
|
||||
</h3>
|
||||
<p class="flex-grow-1">
|
||||
Use Rust to supercharge your JavaScript, one module at a time.
|
||||
Publish to npm, bundle with webpack, and you’re off to the races.
|
||||
</p>
|
||||
<a href="/what/wasm" class="button button-secondary">Writing Web Apps</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row flex-column-l justify-between-l mw8 measure-wide-l w-100 mt5 mt2-l pl4-l">
|
||||
<div class="v-top tc-l">
|
||||
<img src="/static/images/networking.svg" alt="a cloud with nodes" class="mw3 mw4-ns">
|
||||
</div>
|
||||
<div class="v-top pl4 pl0-l pt0 pt3-l measure-wide-l flex-l flex-column-l flex-auto-l justify-between-l">
|
||||
<h3 class="tc-l">
|
||||
Networking
|
||||
</h3>
|
||||
<p class="flex-grow-1">
|
||||
Predictable performance. Tiny resource footprint. Rock-solid reliability.
|
||||
Rust is great for network services.
|
||||
</p>
|
||||
<a href="/what/networking" class="button button-secondary">Working On Servers</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row flex-column-l justify-between-l mw8 measure-wide-l w-100 mt5 mt2-l pl4-l">
|
||||
<div class="v-top tc-l">
|
||||
<img src="/static/images/embedded.svg" alt="an embedded device chip" class="mw3 mw4-ns">
|
||||
</div>
|
||||
<div class="v-top pl4 pl0-l pt0 pt3-l measure-wide-l flex-l flex-column-l flex-auto-l justify-between-l">
|
||||
<h3 class="tc-l">
|
||||
Embedded
|
||||
</h3>
|
||||
<p class="flex-grow-1">
|
||||
Targeting low-resource devices?
|
||||
Need low-level control without giving up high-level conveniences?
|
||||
Rust has you covered.
|
||||
</p>
|
||||
<a href="/what/embedded" class="button button-secondary">Starting With Embedded</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="white production">
|
||||
<div class="w-100 mw-none ph3 mw8-m mw9-l center">
|
||||
<header>
|
||||
<h2>Rust in production</h2>
|
||||
<div class="highlight"></div>
|
||||
</header>
|
||||
<div class="description">
|
||||
<p class="lh-copy f2">
|
||||
Hundreds of companies around the world are using Rust in production
|
||||
today for fast, low-resource, cross-platform solutions. <strong>From startups to large
|
||||
corporations, from embedded devices to scalable web services, Rust is a great fit.</strong>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="get-involved red">
|
||||
<div class="w-100 mw-none ph3 mw8-m mw9-l center f3">
|
||||
<header>
|
||||
<h2>Get involved</h2>
|
||||
<div class="highlight"></div>
|
||||
</header>
|
||||
<div class="flex flex-column flex-row-l">
|
||||
<div id="read-rust" class="mw-50-l mr4-l pt0 flex flex-column justify-between-l">
|
||||
<h3>Read Rust</h3>
|
||||
<p class="flex-grow-1">We love documentation! Take a look at the books available online, as well as key blog posts and user guides.</p>
|
||||
<a href="learn" class="button button-secondary">Read the book</a>
|
||||
</div>
|
||||
<div id="watch-rust" class="mw-50-l pt3 pt0-l flex flex-column justify-between-l">
|
||||
<h3>Watch Rust</h3>
|
||||
<p class="flex-grow-1">The Rust community has a dedicated YouTube channel collecting a huge range of presentations and
|
||||
tutorials.</p>
|
||||
<a href="https://www.youtube.com/channel/UCaYhcUwRBNscFNUKTjgPFiA" class="button button-secondary">Watch the Videos</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pt3">
|
||||
<h3>Contribute code</h3>
|
||||
<p>
|
||||
Rust is truly a community effort, and we welcome contribution from hobbyists and production users, from
|
||||
newcomers and seasoned professionals. Come help us make the Rust experience even better!
|
||||
</p>
|
||||
<a href="https://rustc-dev-guide.rust-lang.org/getting-started.html" class="button button-secondary">
|
||||
Read Contribution Guide
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="white thanks">
|
||||
<div class="w-100 mw-none ph3 mw8-m mw9-l center">
|
||||
<header>
|
||||
<h2>Thanks</h2>
|
||||
<div class="highlight"></div>
|
||||
</header>
|
||||
<div class="description">
|
||||
<p class="lh-copy f2">
|
||||
Rust would not exist without the generous contributions of time, work, and resources from individuals and companies. We are very grateful for the support!
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-column flex-row-l">
|
||||
<div id="individual-code" class="mw-50-l mr4-l pt0 flex flex-column justify-between-l">
|
||||
<h3>Individuals</h3>
|
||||
<p class="flex-grow-1">Rust is a community project and is very thankful for the many community contributions it receives.</p>
|
||||
<a href="https://thanks.rust-lang.org/" class="button button-secondary">See individual contributors</a>
|
||||
</div>
|
||||
<div id="company-sponsorships" class="mw-50-l pt3 pt0-l flex flex-column justify-between-l">
|
||||
<h3>Corporate sponsors</h3>
|
||||
<p class="flex-grow-1">The Rust project receives support from companies through the Rust Foundation.</p>
|
||||
<a href="https://foundation.rust-lang.org/members" class="button button-secondary">See Foundation members</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
<footer>
|
||||
<div class="w-100 mw-none ph3 mw8-m mw9-l center f3">
|
||||
<div class="flex flex-column flex-row-l pv0-l">
|
||||
<div class="flex flex-column mw8 w-100 measure-wide-l pv2 pv5-m pv2-ns ph4-m ph4-l" id="get-help">
|
||||
<h4>Get help!</h4>
|
||||
<ul>
|
||||
<li><a href="/learn">Documentation</a></li>
|
||||
<li><a href="http://forge.rust-lang.org">Rust Forge (Contributor Documentation)</a></li>
|
||||
<li><a href="https://users.rust-lang.org">Ask a Question on the Users Forum</a></li>
|
||||
</ul>
|
||||
<div class="languages">
|
||||
<div class="select">
|
||||
<label for="language-footer" class="hidden">Language</label>
|
||||
<select id="language-footer">
|
||||
<option title="English (en-US)" value="en-US">English (en-US)</option>
|
||||
<option title="Español (es)" value="es">Español (es)</option>
|
||||
<option title="Français (fr)" value="fr">Français (fr)</option>
|
||||
<option title="Italiano (it)" value="it">Italiano (it)</option>
|
||||
<option title="日本語 (ja)" value="ja">日本語 (ja)</option>
|
||||
<option title="Português (pt-BR)" value="pt-BR">Português (pt-BR)</option>
|
||||
<option title="Русский (ru)" value="ru">Русский (ru)</option>
|
||||
<option title="Türkçe (tr)" value="tr">Türkçe (tr)</option>
|
||||
<option title="简体中文 (zh-CN)" value="zh-CN">简体中文 (zh-CN)</option>
|
||||
<option title="正體中文 (zh-TW)" value="zh-TW">正體中文 (zh-TW)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-column mw8 w-100 measure-wide-l pv2 pv5-m pv2-ns ph4-m ph4-l">
|
||||
<h4>Terms and policies</h4>
|
||||
<ul>
|
||||
<li><a href="/policies/code-of-conduct">Code of Conduct</a></li>
|
||||
<li><a href="/policies/licenses">Licenses</a></li>
|
||||
<li><a href="https://foundation.rust-lang.org/policies/logo-policy-and-media-guide/">Logo Policy and Media Guide</a></li>
|
||||
<li><a href="/policies/security">Security Disclosures</a></li>
|
||||
<li><a href="https://foundation.rust-lang.org/policies/privacy-policy/">Privacy Notice</a>
|
||||
</li>
|
||||
<li><a href="/policies">All Policies</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="flex flex-column mw8 w-100 measure-wide-l pv2 pv5-m pv2-ns ph4-m ph4-l">
|
||||
<h4>Social</h4>
|
||||
<div class="flex flex-row flex-wrap items-center">
|
||||
<a rel="me" href="https://social.rust-lang.org/@rust" target="_blank"><img src="/static/images/mastodon.svg" alt="Mastodon" title="Mastodon"></a>
|
||||
<a rel="me" href="https://bsky.app/profile/rust-lang.org" target="_blank"><img src="/static/images/bluesky.svg" alt="Bluesky" title="Bluesky"></a>
|
||||
<a href="https://www.youtube.com/channel/UCaYhcUwRBNscFNUKTjgPFiA" target="_blank"><img class="pv2" src="/static/images/youtube.svg" alt="youtube logo" title="YouTube"></a>
|
||||
<a href="https://github.com/rust-lang" target="_blank"><img src="/static/images/github.svg" alt="github logo" title="GitHub"></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="attribution">
|
||||
<p>
|
||||
Maintained by the Rust Team. See a bug?
|
||||
<a target="_blank" href="https://github.com/rust-lang/www.rust-lang.org/issues/new/choose">File an issue!</a>
|
||||
</p>
|
||||
<p>Web site built every day at 22:00 UTC</p>
|
||||
<p>Looking for the <a href="https://prev.rust-lang.org">previous website</a>?</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="/static/scripts/languages.js"></script>
|
||||
|
||||
|
||||
</body></html>
|
||||
231
tmp/rust_release.html
Normal file
231
tmp/rust_release.html
Normal file
@@ -0,0 +1,231 @@
|
||||
<html lang="en" data-theme="dark"><head>
|
||||
<meta charset="utf-8">
|
||||
<title>Announcing Rust 1.92.0 | Rust Blog</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<meta name="description" content="Empowering everyone to build reliable and efficient software.">
|
||||
<!-- Twitter card -->
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:site" content="@rustlang">
|
||||
<meta name="twitter:creator" content="@rustlang">
|
||||
<meta name="twitter:title" content="Announcing Rust 1.92.0 | Rust Blog">
|
||||
<meta name="twitter:description" content="Empowering everyone to build reliable and efficient software.">
|
||||
<meta name="twitter:image" content="https://www.rust-lang.org/static/images/rust-social.jpg">
|
||||
|
||||
<!-- Facebook OpenGraph -->
|
||||
<meta property="og:title" content="Announcing Rust 1.92.0 | Rust Blog">
|
||||
<meta property="og:description" content="Empowering everyone to build reliable and efficient software.">
|
||||
<meta property="og:image" content="https://www.rust-lang.org/static/images/rust-social-wide.jpg">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:locale" content="en_US">
|
||||
|
||||
<!-- styles -->
|
||||
<link rel="stylesheet" href="https://blog.rust-lang.org/styles/skeleton.css">
|
||||
<link rel="stylesheet" href="https://blog.rust-lang.org/styles/tachyons.css">
|
||||
<link rel="stylesheet" href="https://blog.rust-lang.org/styles/fonts.css">
|
||||
<link rel="stylesheet" href="https://blog.rust-lang.org/styles/app.css">
|
||||
<link rel="stylesheet" type="text/css" id="syntax-theme" href="/styles/syntax-theme-dark.css">
|
||||
|
||||
<!-- stylesheet for user agents without js -->
|
||||
<noscript>
|
||||
<link rel="stylesheet" href="https://blog.rust-lang.org/styles/noscript.css">
|
||||
<link rel="stylesheet" type="text/css" href="/styles/syntax-theme-dark.css" media="(prefers-color-scheme: dark)" />
|
||||
<link rel="stylesheet" type="text/css" href="/styles/syntax-theme-light.css" media="(prefers-color-scheme: light)" />
|
||||
</noscript>
|
||||
|
||||
<!-- favicon -->
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="https://blog.rust-lang.org/images/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="https://blog.rust-lang.org/images/favicon-16x16.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="https://blog.rust-lang.org/images/favicon-32x32.png">
|
||||
<link rel="icon" type="image/svg+xml" href="https://blog.rust-lang.org/images/favicon.svg">
|
||||
<link rel="manifest" href="https://blog.rust-lang.org/images/site.webmanifest">
|
||||
<link rel="mask-icon" href="https://blog.rust-lang.org/images/safari-pinned-tab.svg" color="#5bbad5">
|
||||
<meta name="msapplication-TileColor" content="#00aba9">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<!-- atom -->
|
||||
<link type="application/atom+xml" rel="alternate" href="https://blog.rust-lang.org/feed.xml" title="Rust Blog">
|
||||
|
||||
<!-- theme switcher -->
|
||||
<script src="https://blog.rust-lang.org/scripts/theme-switch.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="flex flex-row justify-center justify-end-l items-center flex-wrap ph2 pl3-ns pr4-ns">
|
||||
<div class="brand flex-auto w-100 w-auto-l self-start tc tl-l">
|
||||
<a href="https://blog.rust-lang.org/">
|
||||
<img class="v-mid ml0-l rust-logo" alt="" src="https://blog.rust-lang.org/images/rust-logo-blk.svg">
|
||||
<span class="dib ml1 ml0-l">Rust Blog</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ul class="nav list w-100 w-auto-l flex flex-none flex-row flex-wrap justify-center justify-end-l items-center pv2 ph0 ph4-ns">
|
||||
<li class="tc pv2 ph2 ph4-ns flex-20-s"><a href="https://www.rust-lang.org">Rust</a></li>
|
||||
<li class="tc pv2 ph2 ph4-ns flex-20-s"><a href="https://www.rust-lang.org/tools/install">Install</a></li>
|
||||
<li class="tc pv2 ph2 ph4-ns flex-20-s"><a href="https://www.rust-lang.org/learn">Learn</a></li>
|
||||
<li class="tc pv2 ph2 ph4-ns flex-20-s"><a href="https://www.rust-lang.org/tools">Tools</a></li>
|
||||
<li class="tc pv2 ph2 ph4-ns flex-20-s"><a href="https://www.rust-lang.org/governance">Governance</a></li>
|
||||
<li class="tc pv2 ph2 ph4-ns flex-20-s"><a href="https://www.rust-lang.org/community">Community</a></li>
|
||||
<button class="theme-icon" onclick="dropdown();" style="display: block;">🖌
|
||||
<ul id="theme-choice">
|
||||
<li class="theme-item" onclick="changeThemeTo('light');">Light</li>
|
||||
<li class="theme-item" onclick="changeThemeTo('dark');">Dark</li>
|
||||
<li class="theme-item" onclick="changeThemeTo('system');">System</li>
|
||||
</ul>
|
||||
</button>
|
||||
<script src="https://blog.rust-lang.org/scripts/theme-switch-post.js"></script>
|
||||
</ul>
|
||||
</nav><section id="Announcing Rust 1.92.0" class="white">
|
||||
<div class="w-100 mw-none ph3 mw8-m mw8-l center f3">
|
||||
<header>
|
||||
<h2>Announcing Rust 1.92.0</h2>
|
||||
<div class="highlight mt2 mb3"></div>
|
||||
</header>
|
||||
|
||||
<div class="publish-date-author">Dec. 11, 2025 · The Rust Release Team
|
||||
|
||||
</div>
|
||||
|
||||
<div class="post">
|
||||
<p>The Rust team is happy to announce a new version of Rust, 1.92.0. Rust is a programming language empowering everyone to build reliable and efficient software.</p>
|
||||
<p>If you have a previous version of Rust installed via <code>rustup</code>, you can get 1.92.0 with:</p>
|
||||
<pre data-lang="console" class="language-console z-code"><code class="language-console" data-lang="console"><span class="z-text z-plain">$ rustup update stable
|
||||
</span></code></pre>
|
||||
<p>If you don't have it already, you can <a href="https://www.rust-lang.org/install.html">get <code>rustup</code></a> from the appropriate page on our website, and check out the <a href="https://doc.rust-lang.org/stable/releases.html#version-1920-2025-12-11">detailed release notes for 1.92.0</a>.</p>
|
||||
<p>If you'd like to help us out by testing future releases, you might consider updating locally to use the beta channel (<code>rustup default beta</code>) or the nightly channel (<code>rustup default nightly</code>). Please <a href="https://github.com/rust-lang/rust/issues/new/choose">report</a> any bugs you might come across!</p>
|
||||
<h2 id="what-s-in-1-92-0-stable"><a class="anchor" href="#what-s-in-1-92-0-stable" aria-hidden="true"></a>
|
||||
What's in 1.92.0 stable</h2>
|
||||
<h3 id="deny-by-default-never-type-lints"><a class="anchor" href="#deny-by-default-never-type-lints" aria-hidden="true"></a>
|
||||
Deny-by-default never type lints</h3>
|
||||
<p>The language and compiler teams continue to work on stabilization of the <a href="https://doc.rust-lang.org/stable/std/primitive.never.html">never type</a>. In this release the <a href="https://doc.rust-lang.org/rustc/lints/listing/deny-by-default.html#never-type-fallback-flowing-into-unsafe"><code>never_type_fallback_flowing_into_unsafe</code></a> and <a href="https://doc.rust-lang.org/rustc/lints/listing/deny-by-default.html#dependency-on-unit-never-type-fallback"><code>dependency_on_unit_never_type_fallback</code></a> future compatibility lints were made deny-by-default, meaning they will cause a compilation error when detected.</p>
|
||||
<p>It's worth noting that while this can result in compilation errors, it is still a <em>lint;</em> these lints can all be <code>#[allow]</code>ed. These lints also will only fire when building the affected crates directly, not when they are built as dependencies (though a warning will be reported by Cargo in such cases).</p>
|
||||
<p>These lints detect code which is likely to be broken by the never type stabilization. It is highly advised to fix them if they are reported in your crate graph.</p>
|
||||
<p>We believe there to be approximately 500 crates affected by this lint. Despite that, we believe this to be acceptable, as lints are not a breaking change and it will allow for stabilizing the never type in the future. For more in-depth justification, see the <a href="https://github.com/rust-lang/rust/pull/146167#issuecomment-3363795006">Language Team's assessment</a>.</p>
|
||||
<h3 id="unused-must-use-no-longer-warns-about-result-uninhabitedtype"><a class="anchor" href="#unused-must-use-no-longer-warns-about-result-uninhabitedtype" aria-hidden="true"></a>
|
||||
<code>unused_must_use</code> no longer warns about <code>Result<(), UninhabitedType></code></h3>
|
||||
<p>Rust's <code>unused_must_use</code> lint warns when ignoring the return value of a function, if the function or its return type is annotated with <code>#[must_use]</code>. For instance, this warns if ignoring a return type of <code>Result</code>, to remind you to use <code>?</code>, or something like <code>.expect("...")</code>.</p>
|
||||
<p>However, some functions return <code>Result</code>, but the error type they use is not actually "inhabited", meaning you cannot construct any values of that type (e.g. the <a href="https://doc.rust-lang.org/std/primitive.never.html"><code>!</code></a> or <a href="https://doc.rust-lang.org/std/convert/enum.Infallible.html"><code>Infallible</code></a> types).</p>
|
||||
<p>The <code>unused_must_use</code> lint now no longer warns on <code>Result<(), UninhabitedType></code>, or on <code>ControlFlow<UninhabitedType, ()></code>. For instance, it will not warn on <code>Result<(), Infallible></code>. This avoids having to check for an error that can never happen.</p>
|
||||
<pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-keyword z-other z-rust">use</span> <span class="z-meta z-path z-rust">core<span class="z-punctuation z-accessor z-rust">::</span></span><span class="z-meta z-path z-rust">convert<span class="z-punctuation z-accessor z-rust">::</span></span>Infallible<span class="z-punctuation z-terminator z-rust">;</span>
|
||||
</span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">can_never_fail</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-></span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">Result</span><span class="z-punctuation z-definition z-generic z-begin z-rust"><</span><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-punctuation z-section z-group z-end z-rust">)</span>, Infallible<span class="z-punctuation z-definition z-generic z-end z-rust">></span></span></span> </span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span>
|
||||
</span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> ...
|
||||
</span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-support z-type z-rust">Ok</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span>
|
||||
</span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span>
|
||||
</span><span class="z-source z-rust">
|
||||
</span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">main</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> </span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span>
|
||||
</span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-support z-function z-rust">can_never_fail</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span>
|
||||
</span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span>
|
||||
</span></code></pre>
|
||||
<p>This is particularly useful with the common pattern of a trait with an associated error type, where the error type may <em>sometimes</em> be infallible:</p>
|
||||
<pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-storage z-type z-trait z-rust">trait</span> <span class="z-entity z-name z-trait z-rust">UsesAssocErrorType</span> <span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span>
|
||||
</span></span></span><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-type z-rust">type</span> <span class="z-entity z-name z-type z-rust">Error</span><span class="z-punctuation z-terminator z-rust">;</span>
|
||||
</span></span></span><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">method</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&</span><span class="z-variable z-parameter z-rust">self</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-></span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">Result</span><span class="z-punctuation z-definition z-generic z-begin z-rust"><</span><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-punctuation z-section z-group z-end z-rust">)</span>, <span class="z-meta z-path z-rust"><span class="z-storage z-type z-rust"><span class="z-storage z-type z-rust">Self</span><span class="z-punctuation z-accessor z-rust">::</span></span></span>Error<span class="z-punctuation z-definition z-generic z-end z-rust">></span></span></span></span><span class="z-punctuation z-terminator z-rust">;</span>
|
||||
</span></span></span><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span>
|
||||
</span><span class="z-source z-rust">
|
||||
</span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-storage z-type z-struct z-rust">struct</span> </span><span class="z-meta z-struct z-rust"><span class="z-entity z-name z-struct z-rust">CannotFail</span></span><span class="z-punctuation z-terminator z-rust">;</span>
|
||||
</span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-storage z-type z-impl z-rust">impl</span> </span><span class="z-meta z-impl z-rust">UsesAssocErrorType <span class="z-keyword z-other z-rust">for</span></span><span class="z-meta z-impl z-rust"> <span class="z-entity z-name z-impl z-rust">CannotFail</span> </span><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span>
|
||||
</span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-type z-rust">type</span> <span class="z-entity z-name z-type z-rust">Error</span> <span class="z-keyword z-operator z-assignment z-rust">=</span> <span class="z-meta z-path z-rust">core<span class="z-punctuation z-accessor z-rust">::</span></span><span class="z-meta z-path z-rust">convert<span class="z-punctuation z-accessor z-rust">::</span></span>Infallible<span class="z-punctuation z-terminator z-rust">;</span>
|
||||
</span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">method</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&</span><span class="z-variable z-parameter z-rust">self</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-></span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">Result</span><span class="z-punctuation z-definition z-generic z-begin z-rust"><</span><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-punctuation z-section z-group z-end z-rust">)</span>, <span class="z-meta z-path z-rust"><span class="z-storage z-type z-rust"><span class="z-storage z-type z-rust">Self</span><span class="z-punctuation z-accessor z-rust">::</span></span></span>Error<span class="z-punctuation z-definition z-generic z-end z-rust">></span></span></span> </span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span>
|
||||
</span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-support z-type z-rust">Ok</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span>
|
||||
</span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span>
|
||||
</span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span>
|
||||
</span><span class="z-source z-rust">
|
||||
</span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-storage z-type z-struct z-rust">struct</span> </span><span class="z-meta z-struct z-rust"><span class="z-entity z-name z-struct z-rust">CanFail</span></span><span class="z-punctuation z-terminator z-rust">;</span>
|
||||
</span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-storage z-type z-impl z-rust">impl</span> </span><span class="z-meta z-impl z-rust">UsesAssocErrorType <span class="z-keyword z-other z-rust">for</span></span><span class="z-meta z-impl z-rust"> <span class="z-entity z-name z-impl z-rust">CanFail</span> </span><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span>
|
||||
</span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-type z-rust">type</span> <span class="z-entity z-name z-type z-rust">Error</span> <span class="z-keyword z-operator z-assignment z-rust">=</span> <span class="z-meta z-path z-rust">std<span class="z-punctuation z-accessor z-rust">::</span></span><span class="z-meta z-path z-rust">io<span class="z-punctuation z-accessor z-rust">::</span></span>Error<span class="z-punctuation z-terminator z-rust">;</span>
|
||||
</span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">method</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&</span><span class="z-variable z-parameter z-rust">self</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-></span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">Result</span><span class="z-punctuation z-definition z-generic z-begin z-rust"><</span><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-punctuation z-section z-group z-end z-rust">)</span>, <span class="z-meta z-path z-rust"><span class="z-storage z-type z-rust"><span class="z-storage z-type z-rust">Self</span><span class="z-punctuation z-accessor z-rust">::</span></span></span>Error<span class="z-punctuation z-definition z-generic z-end z-rust">></span></span></span> </span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span>
|
||||
</span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-support z-type z-rust">Err</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-meta z-path z-rust">std<span class="z-punctuation z-accessor z-rust">::</span></span><span class="z-meta z-path z-rust">io<span class="z-punctuation z-accessor z-rust">::</span></span><span class="z-meta z-path z-rust">Error<span class="z-punctuation z-accessor z-rust">::</span></span>other<span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-string z-quoted z-double z-rust"><span class="z-punctuation z-definition z-string z-begin z-rust">"</span>something went wrong<span class="z-punctuation z-definition z-string z-end z-rust">"</span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span>
|
||||
</span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span>
|
||||
</span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span>
|
||||
</span><span class="z-source z-rust">
|
||||
</span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">main</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> </span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span>
|
||||
</span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> CannotFail<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">method</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> No warning
|
||||
</span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> CanFail<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">method</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> Warning: unused `Result` that must be used
|
||||
</span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span>
|
||||
</span></code></pre>
|
||||
<h3 id="emit-unwind-tables-even-when-cpanic-abort-is-enabled-on-linux"><a class="anchor" href="#emit-unwind-tables-even-when-cpanic-abort-is-enabled-on-linux" aria-hidden="true"></a>
|
||||
Emit unwind tables even when <code>-Cpanic=abort</code> is enabled on linux</h3>
|
||||
<p>Backtraces with <code>-Cpanic=abort</code> previously worked in Rust 1.22 but were broken in Rust 1.23, as we stopped emitting unwind tables with <code>-Cpanic=abort</code>. In Rust 1.45 a workaround in the form of <code>-Cforce-unwind-tables=yes</code> was stabilized.</p>
|
||||
<p>In Rust 1.92 unwind tables will be emitted by default even when <code>-Cpanic=abort</code> is specified, allowing for backtraces to work properly. If unwind tables are not desired then users should use <code>-Cforce-unwind-tables=no</code> to explicitly disable them being emitted.</p>
|
||||
<h3 id="validate-input-to-macro-export"><a class="anchor" href="#validate-input-to-macro-export" aria-hidden="true"></a>
|
||||
Validate input to <code>#[macro_export]</code></h3>
|
||||
<p>Over the past few releases, many changes were made to the way built-in attributes are processed in the compiler. This should greatly improve the error messages and warnings Rust gives for built-in attributes and especially make these diagnostics more consistent among all of the over 100 built-in attributes.</p>
|
||||
<p>To give a small example, in this release specifically, Rust became stricter in checking what arguments are allowed to <code>macro_export</code> by <a href="https://github.com/rust-lang/rust/pull/143857">upgrading that check to a "deny-by-default lint" that will be reported in dependencies</a>.</p>
|
||||
<h3 id="stabilized-apis"><a class="anchor" href="#stabilized-apis" aria-hidden="true"></a>
|
||||
Stabilized APIs</h3>
|
||||
<ul>
|
||||
<li><a href="https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.div_ceil"><code>NonZero<u{N}>::div_ceil</code></a></li>
|
||||
<li><a href="https://doc.rust-lang.org/stable/std/panic/struct.Location.html#method.file_as_c_str"><code>Location::file_as_c_str</code></a></li>
|
||||
<li><a href="https://doc.rust-lang.org/stable/std/sync/struct.RwLockWriteGuard.html#method.downgrade"><code>RwLockWriteGuard::downgrade</code></a></li>
|
||||
<li><a href="https://doc.rust-lang.org/stable/std/boxed/struct.Box.html#method.new_zeroed"><code>Box::new_zeroed</code></a></li>
|
||||
<li><a href="https://doc.rust-lang.org/stable/std/boxed/struct.Box.html#method.new_zeroed_slice"><code>Box::new_zeroed_slice</code></a></li>
|
||||
<li><a href="https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.new_zeroed"><code>Rc::new_zeroed</code></a></li>
|
||||
<li><a href="https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.new_zeroed_slice"><code>Rc::new_zeroed_slice</code></a></li>
|
||||
<li><a href="https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.new_zeroed"><code>Arc::new_zeroed</code></a></li>
|
||||
<li><a href="https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.new_zeroed_slice"><code>Arc::new_zeroed_slice</code></a></li>
|
||||
<li><a href="https://doc.rust-lang.org/stable/std/collections/btree_map/enum.Entry.html#method.insert_entry"><code>btree_map::Entry::insert_entry</code></a></li>
|
||||
<li><a href="https://doc.rust-lang.org/stable/std/collections/btree_map/struct.VacantEntry.html#method.insert_entry"><code>btree_map::VacantEntry::insert_entry</code></a></li>
|
||||
<li><a href="https://doc.rust-lang.org/stable/proc_macro/struct.TokenStream.html#impl-Extend%3CGroup%3E-for-TokenStream"><code>impl Extend<proc_macro::Group> for proc_macro::TokenStream</code></a></li>
|
||||
<li><a href="https://doc.rust-lang.org/stable/proc_macro/struct.TokenStream.html#impl-Extend%3CLiteral%3E-for-TokenStream"><code>impl Extend<proc_macro::Literal> for proc_macro::TokenStream</code></a></li>
|
||||
<li><a href="https://doc.rust-lang.org/stable/proc_macro/struct.TokenStream.html#impl-Extend%3CPunct%3E-for-TokenStream"><code>impl Extend<proc_macro::Punct> for proc_macro::TokenStream</code></a></li>
|
||||
<li><a href="https://doc.rust-lang.org/stable/proc_macro/struct.TokenStream.html#impl-Extend%3CIdent%3E-for-TokenStream"><code>impl Extend<proc_macro::Ident> for proc_macro::TokenStream</code></a></li>
|
||||
</ul>
|
||||
<p>These previously stable APIs are now stable in const contexts:</p>
|
||||
<ul>
|
||||
<li><a href="https://doc.rust-lang.org/stable/std/primitive.slice.html#method.rotate_left"><code><[_]>::rotate_left</code></a></li>
|
||||
<li><a href="https://doc.rust-lang.org/stable/std/primitive.slice.html#method.rotate_right"><code><[_]>::rotate_right</code></a></li>
|
||||
</ul>
|
||||
<h3 id="other-changes"><a class="anchor" href="#other-changes" aria-hidden="true"></a>
|
||||
Other changes</h3>
|
||||
<p>Check out everything that changed in <a href="https://github.com/rust-lang/rust/releases/tag/1.92.0">Rust</a>, <a href="https://doc.rust-lang.org/nightly/cargo/CHANGELOG.html#cargo-192-2025-12-11">Cargo</a>, and <a href="https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md#rust-192">Clippy</a>.</p>
|
||||
<h2 id="contributors-to-1-92-0"><a class="anchor" href="#contributors-to-1-92-0" aria-hidden="true"></a>
|
||||
Contributors to 1.92.0</h2>
|
||||
<p>Many people came together to create Rust 1.92.0. We couldn't have done it without all of you. <a href="https://thanks.rust-lang.org/rust/1.92.0/">Thanks!</a></p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="footer">
|
||||
<div class="w-100 mw-none ph3 mw8-m mw9-l center f3">
|
||||
<div class="row">
|
||||
<div class="four columns mt3 mt0-l" id="get-help">
|
||||
<h4>Get help!</h4>
|
||||
<ul>
|
||||
<li><a href="https://doc.rust-lang.org" target="_blank" rel="noopener">Documentation</a></li>
|
||||
<li><a href="mailto:core-team@rust-lang.org">Contact the Rust Team</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="four columns mt3 mt0-l">
|
||||
<h4>Terms and policies</h4>
|
||||
<ul>
|
||||
<li><a href="https://www.rust-lang.org/policies/code-of-conduct">Code of Conduct</a></li>
|
||||
<li><a href="https://www.rust-lang.org/policies/licenses">Licenses</a></li>
|
||||
<li><a href="https://www.rust-lang.org/policies/media-guide">Logo Policy and Media Guide</a></li>
|
||||
<li><a href="https://www.rust-lang.org/policies/security">Security Disclosures</a></li>
|
||||
<li><a href="https://www.rust-lang.org/policies">All Policies</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="four columns mt3 mt0-l">
|
||||
<h4>Social</h4>
|
||||
<div class="flex flex-row flex-wrap">
|
||||
<a rel="me" href="https://social.rust-lang.org/@rust" target="_blank"><img src="https://blog.rust-lang.org/images/mastodon.svg" alt="Mastodon"></a>
|
||||
<a rel="me" href="https://bsky.app/profile/rust-lang.org" target="_blank"><img src="https://blog.rust-lang.org/images/bluesky.svg" alt="Bluesky"></a>
|
||||
<a href="https://www.youtube.com/channel/UCaYhcUwRBNscFNUKTjgPFiA" target="_blank" rel="noopener"><img style="padding-top: 6px; padding-bottom:6px" src="https://blog.rust-lang.org/images/youtube.svg" alt="YouTube"></a>
|
||||
<a href="https://discord.gg/rust-lang" target="_blank" rel="noopener"><img src="https://blog.rust-lang.org/images/discord.svg" alt="Discord"></a>
|
||||
<a href="https://github.com/rust-lang" target="_blank" rel="noopener"><img src="https://blog.rust-lang.org/images/github.svg" alt="GitHub"></a>
|
||||
</div>
|
||||
<h4 class="mt4 mb3">RSS</h4>
|
||||
<ul>
|
||||
<li><a href="https://blog.rust-lang.org/feed.xml">Main Blog</a></li>
|
||||
<li><a href="https://blog.rust-lang.org/inside-rust/feed.xml">"Inside Rust" Blog</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="attribution">
|
||||
Maintained by the Rust Team. See a typo?
|
||||
<a href="https://github.com/rust-lang/blog.rust-lang.org/edit/main/content/Rust-1.92.0.md" target="_blank" rel="noopener">Send a fix here</a>!
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
</body></html>
|
||||
25
tmp/search_results.html
Normal file
25
tmp/search_results.html
Normal file
File diff suppressed because one or more lines are too long
335
tmp/tokio_channels.html
Normal file
335
tmp/tokio_channels.html
Normal file
File diff suppressed because one or more lines are too long
344
tmp/tokio_stream_docs.html
Normal file
344
tmp/tokio_stream_docs.html
Normal file
@@ -0,0 +1,344 @@
|
||||
<html lang="en" data-theme="dark" data-docs-rs-theme="dark"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Stream utilities for Tokio."><title>tokio_stream - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="/-/rustdoc.static/${f}">`).join(""))</script><link rel="preload" as="font" type="font/woff2" href="/-/rustdoc.static/SourceSerif4-Regular-6b053e98.ttf.woff2"><link rel="preload" as="font" type="font/woff2" href="/-/rustdoc.static/FiraSans-Italic-81dc35de.woff2"><link rel="preload" as="font" type="font/woff2" href="/-/rustdoc.static/FiraSans-Regular-0fe48ade.woff2"><link rel="preload" as="font" type="font/woff2" href="/-/rustdoc.static/FiraSans-MediumItalic-ccf7e434.woff2"><link rel="preload" as="font" type="font/woff2" href="/-/rustdoc.static/FiraSans-Medium-e1aa3f0a.woff2"><link rel="preload" as="font" type="font/woff2" href="/-/rustdoc.static/SourceCodePro-Regular-8badfe75.ttf.woff2"><link rel="preload" as="font" type="font/woff2" href="/-/rustdoc.static/SourceCodePro-Semibold-aa29a496.ttf.woff2"><link rel="stylesheet" href="/-/rustdoc.static/normalize-9960930a.css"><link rel="stylesheet" href="/-/static/vendored.css?0-1-0-7bfb09c2-2026-01-03" media="all"><link rel="stylesheet" href="/-/rustdoc.static/rustdoc-2eb46af5.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="/-/rustdoc.static/" data-current-crate="tokio_stream" data-themes="" data-resource-suffix="-20260103-1.94.0-nightly-7ecabfaaf" data-rustdoc-version="1.94.0-nightly (7ecabfaaf 2026-01-03)" data-channel="nightly" data-search-js="search-9e2438ea.js" data-stringdex-js="stringdex-b897f86f.js" data-settings-js="settings-c38705f0.js"><script src="/-/rustdoc.static/storage-e2aeef58.js"></script><script defer="" src="../crates-20260103-1.94.0-nightly-7ecabfaaf.js"></script><script defer="" src="/-/rustdoc.static/main-7bab91a1.js"></script><noscript><link rel="stylesheet" href="/-/rustdoc.static/noscript-ffcac47a.css"></noscript><link rel="alternate icon" type="image/png" href="/-/rustdoc.static/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="/-/rustdoc.static/favicon-044be391.svg"><link rel="stylesheet" href="/-/static/rustdoc-2025-08-20.css?0-1-0-7bfb09c2-2026-01-03" media="all"><link rel="stylesheet" href="/-/static/font-awesome.css?0-1-0-7bfb09c2-2026-01-03" media="all">
|
||||
|
||||
<link rel="search" href="/-/static/opensearch.xml" type="application/opensearchdescription+xml" title="Docs.rs">
|
||||
|
||||
<script type="text/javascript">(function() {
|
||||
function applyTheme(theme) {
|
||||
if (theme) {
|
||||
document.documentElement.dataset.docsRsTheme = theme;
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("storage", ev => {
|
||||
if (ev.key === "rustdoc-theme") {
|
||||
applyTheme(ev.newValue);
|
||||
}
|
||||
});
|
||||
|
||||
// see ./storage-change-detection.html for details
|
||||
window.addEventListener("message", ev => {
|
||||
if (ev.data && ev.data.storage && ev.data.storage.key === "rustdoc-theme") {
|
||||
applyTheme(ev.data.storage.value);
|
||||
}
|
||||
});
|
||||
|
||||
applyTheme(window.localStorage.getItem("rustdoc-theme"));
|
||||
})();</script></head><div style="display: none; position: fixed; width: 100%; height: 100%; z-index: 1;"></div><body class="rustdoc-page">
|
||||
<div class="nav-container">
|
||||
<div class="container">
|
||||
<div class="pure-menu pure-menu-horizontal" role="navigation" aria-label="Main navigation">
|
||||
<form action="/releases/search" method="GET" id="nav-search-form" class="landing-search-form-nav ">
|
||||
|
||||
|
||||
<a href="/" class="pure-menu-heading pure-menu-link docsrs-logo" aria-label="Docs.rs">
|
||||
<span title="Docs.rs"><span class="fa fa-solid fa-cubes " aria-hidden="true"></span></span>
|
||||
<span class="title">Docs.rs</span>
|
||||
</a><ul class="pure-menu-list">
|
||||
<script id="crate-metadata" type="application/json">
|
||||
|
||||
{
|
||||
"name": "tokio-stream",
|
||||
"version": "0.1.18"
|
||||
}
|
||||
</script><li class="pure-menu-item pure-menu-has-children">
|
||||
<a href="#" class="pure-menu-link crate-name" title="Utilities to work with `Stream` and `tokio`.
|
||||
" aria-haspopup="menu">
|
||||
<span class="fa fa-solid fa-cube " aria-hidden="true"></span>
|
||||
<span class="title">tokio-stream-0.1.18</span>
|
||||
</a><div class="pure-menu-children package-details-menu" role="menu">
|
||||
|
||||
<ul class="pure-menu-list menu-item-divided">
|
||||
<li class="pure-menu-heading" id="crate-title">
|
||||
tokio-stream 0.1.18
|
||||
<span id="clipboard" class="svg-clipboard" title="Copy crate name and version information"></span>
|
||||
</li><li class="pure-menu-item">
|
||||
<a href="/tokio-stream/0.1.18/tokio_stream/" class="pure-menu-link description" id="permalink" title="Get a link to this specific version"><span class="fa fa-solid fa-link " aria-hidden="true"></span> Permalink
|
||||
</a>
|
||||
</li><li class="pure-menu-item">
|
||||
<a href="/crate/tokio-stream/latest" class="pure-menu-link description" title="See tokio-stream in docs.rs">
|
||||
<span class="fa fa-solid fa-cube " aria-hidden="true"></span> Docs.rs crate page
|
||||
</a>
|
||||
</li><li class="pure-menu-item">
|
||||
<span class="pure-menu-link description"><span class="fa fa-solid fa-scale-unbalanced-flip " aria-hidden="true"></span>
|
||||
<a href="https://spdx.org/licenses/MIT" class="pure-menu-sublink">MIT</a></span>
|
||||
</li></ul>
|
||||
|
||||
<div class="pure-g menu-item-divided">
|
||||
<div class="pure-u-1-2 right-border">
|
||||
<ul class="pure-menu-list">
|
||||
<li class="pure-menu-heading">Links</li>
|
||||
|
||||
<li class="pure-menu-item">
|
||||
<a href="https://tokio.rs" class="pure-menu-link">
|
||||
<span class="fa fa-solid fa-house " aria-hidden="true"></span> Homepage
|
||||
</a>
|
||||
</li><li class="pure-menu-item">
|
||||
<a href="https://github.com/tokio-rs/tokio" class="pure-menu-link">
|
||||
<span class="fa fa-solid fa-code-branch " aria-hidden="true"></span> Repository
|
||||
</a>
|
||||
</li><li class="pure-menu-item">
|
||||
<a href="https://crates.io/crates/tokio-stream" class="pure-menu-link" title="See tokio-stream in crates.io">
|
||||
<span class="fa fa-solid fa-cube " aria-hidden="true"></span> crates.io
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li class="pure-menu-item">
|
||||
<a href="/crate/tokio-stream/latest/source/" title="Browse source of tokio-stream-0.1.18" class="pure-menu-link">
|
||||
<span class="fa fa-solid fa-folder-open " aria-hidden="true"></span> Source
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div><div class="pure-u-1-2">
|
||||
<ul class="pure-menu-list" id="topbar-owners">
|
||||
<li class="pure-menu-heading">Owners</li><li class="pure-menu-item">
|
||||
<a href="https://crates.io/users/carllerche" class="pure-menu-link">
|
||||
<span class="fa fa-solid fa-user " aria-hidden="true"></span> carllerche
|
||||
</a>
|
||||
</li><li class="pure-menu-item">
|
||||
<a href="https://crates.io/users/Darksonn" class="pure-menu-link">
|
||||
<span class="fa fa-solid fa-user " aria-hidden="true"></span> Darksonn
|
||||
</a>
|
||||
</li><li class="pure-menu-item">
|
||||
<a href="https://crates.io/teams/github:tokio-rs:core" class="pure-menu-link">
|
||||
<span class="fa fa-solid fa-user " aria-hidden="true"></span> github:tokio-rs:core
|
||||
</a>
|
||||
</li></ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pure-g menu-item-divided">
|
||||
<div class="pure-u-1-2 right-border">
|
||||
<ul class="pure-menu-list">
|
||||
<li class="pure-menu-heading">Dependencies</li>
|
||||
|
||||
|
||||
<li class="pure-menu-item">
|
||||
<div class="pure-menu pure-menu-scrollable sub-menu" tabindex="-1">
|
||||
<ul class="pure-menu-list">
|
||||
<li class="pure-menu-item"><a href="/futures-core/^0.3.0/" class="pure-menu-link">
|
||||
futures-core ^0.3.0
|
||||
|
||||
<i class="dependencies normal">normal</i>
|
||||
|
||||
|
||||
</a>
|
||||
</li><li class="pure-menu-item"><a href="/pin-project-lite/^0.2.11/" class="pure-menu-link">
|
||||
pin-project-lite ^0.2.11
|
||||
|
||||
<i class="dependencies normal">normal</i>
|
||||
|
||||
|
||||
</a>
|
||||
</li><li class="pure-menu-item"><a href="/tokio/^1.15.0/" class="pure-menu-link">
|
||||
tokio ^1.15.0
|
||||
|
||||
<i class="dependencies normal">normal</i>
|
||||
|
||||
|
||||
</a>
|
||||
</li><li class="pure-menu-item"><a href="/tokio-util/^0.7.0/" class="pure-menu-link">
|
||||
tokio-util ^0.7.0
|
||||
|
||||
<i class="dependencies normal">normal</i>
|
||||
|
||||
<i>optional</i>
|
||||
|
||||
|
||||
</a>
|
||||
</li><li class="pure-menu-item"><a href="/async-stream/^0.3/" class="pure-menu-link">
|
||||
async-stream ^0.3
|
||||
|
||||
<i class="dependencies dev">dev</i>
|
||||
|
||||
|
||||
</a>
|
||||
</li><li class="pure-menu-item"><a href="/futures/^0.3/" class="pure-menu-link">
|
||||
futures ^0.3
|
||||
|
||||
<i class="dependencies dev">dev</i>
|
||||
|
||||
|
||||
</a>
|
||||
</li><li class="pure-menu-item"><a href="/parking_lot/^0.12.0/" class="pure-menu-link">
|
||||
parking_lot ^0.12.0
|
||||
|
||||
<i class="dependencies dev">dev</i>
|
||||
|
||||
|
||||
</a>
|
||||
</li><li class="pure-menu-item"><a href="/tokio/^1.2.0/" class="pure-menu-link">
|
||||
tokio ^1.2.0
|
||||
|
||||
<i class="dependencies dev">dev</i>
|
||||
|
||||
|
||||
</a>
|
||||
</li><li class="pure-menu-item"><a href="/tokio-test/^0.4/" class="pure-menu-link">
|
||||
tokio-test ^0.4
|
||||
|
||||
<i class="dependencies dev">dev</i>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="pure-u-1-2">
|
||||
<ul class="pure-menu-list">
|
||||
<li class="pure-menu-heading">Versions</li>
|
||||
|
||||
<li class="pure-menu-item">
|
||||
<div class="pure-menu pure-menu-scrollable sub-menu" id="releases-list" tabindex="-1" data-url="/crate/tokio-stream/latest/menus/releases/tokio_stream/">
|
||||
<span class="rotate"><span class="fa fa-solid fa-spinner " aria-hidden="true"></span></span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="pure-g">
|
||||
<div class="pure-u-1">
|
||||
<ul class="pure-menu-list">
|
||||
<li>
|
||||
<a href="/crate/tokio-stream/latest" class="pure-menu-link">
|
||||
<b>100%</b>
|
||||
of the crate is documented
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div></div>
|
||||
</li><li class="pure-menu-item pure-menu-has-children">
|
||||
<a href="#" class="pure-menu-link" aria-label="Platform" aria-haspopup="menu">
|
||||
<span class="fa fa-solid fa-gears " aria-hidden="true"></span>
|
||||
<span class="title">Platform</span>
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="pure-menu-children" id="platforms" data-url="/crate/tokio-stream/latest/menus/platforms/tokio_stream/" role="menu"><li class="pure-menu-item">
|
||||
<a href="/crate/tokio-stream/latest/target-redirect/aarch64-apple-darwin/tokio_stream/" class="pure-menu-link" data-fragment="retain" rel="nofollow">aarch64-apple-darwin</a>
|
||||
</li><li class="pure-menu-item">
|
||||
<a href="/crate/tokio-stream/latest/target-redirect/aarch64-unknown-linux-gnu/tokio_stream/" class="pure-menu-link" data-fragment="retain" rel="nofollow">aarch64-unknown-linux-gnu</a>
|
||||
</li><li class="pure-menu-item">
|
||||
<a href="/crate/tokio-stream/latest/target-redirect/i686-pc-windows-msvc/tokio_stream/" class="pure-menu-link" data-fragment="retain" rel="nofollow">i686-pc-windows-msvc</a>
|
||||
</li><li class="pure-menu-item">
|
||||
<a href="/crate/tokio-stream/latest/target-redirect/x86_64-pc-windows-msvc/tokio_stream/" class="pure-menu-link" data-fragment="retain" rel="nofollow">x86_64-pc-windows-msvc</a>
|
||||
</li><li class="pure-menu-item">
|
||||
<a href="/crate/tokio-stream/latest/target-redirect/tokio_stream/" class="pure-menu-link" data-fragment="retain" rel="nofollow">x86_64-unknown-linux-gnu</a>
|
||||
</li></ul>
|
||||
</li><li class="pure-menu-item">
|
||||
<a href="/crate/tokio-stream/latest/features" title="Browse available feature flags of tokio-stream-0.1.18" class="pure-menu-link">
|
||||
<span class="fa fa-solid fa-flag " aria-hidden="true"></span>
|
||||
<span class="title">Feature flags</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
</ul><div class="spacer"></div>
|
||||
|
||||
|
||||
|
||||
<ul class="pure-menu-list">
|
||||
<li class="pure-menu-item pure-menu-has-children">
|
||||
<a href="#" class="pure-menu-link" aria-label="docs.rs" aria-haspopup="menu">docs.rs</a>
|
||||
<ul class="pure-menu-children aligned-icons" role="menu"><li class="pure-menu-item"><a class="pure-menu-link" href="/about"><span class="fa fa-solid fa-circle-info " aria-hidden="true"></span> About docs.rs</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/about/badges"><span class="fa fa-brands fa-fonticons " aria-hidden="true"></span> Badges</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/about/builds"><span class="fa fa-solid fa-gears " aria-hidden="true"></span> Builds</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/about/metadata"><span class="fa fa-solid fa-table " aria-hidden="true"></span> Metadata</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/about/redirections"><span class="fa fa-solid fa-road " aria-hidden="true"></span> Shorthand URLs</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/about/download"><span class="fa fa-solid fa-download " aria-hidden="true"></span> Download</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/about/rustdoc-json"><span class="fa fa-solid fa-file-code " aria-hidden="true"></span> Rustdoc JSON</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/releases/queue"><span class="fa fa-solid fa-gears " aria-hidden="true"></span> Build queue</a></li><li class="pure-menu-item"><a class="pure-menu-link" href="https://foundation.rust-lang.org/policies/privacy-policy/#docs.rs" target="_blank"><span class="fa fa-solid fa-shield-halved " aria-hidden="true"></span> Privacy policy</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="pure-menu-list"><li class="pure-menu-item pure-menu-has-children">
|
||||
<a href="#" class="pure-menu-link" aria-label="Rust" aria-haspopup="menu">Rust</a>
|
||||
<ul class="pure-menu-children" role="menu">
|
||||
<li class="pure-menu-item"><a class="pure-menu-link" href="https://www.rust-lang.org/" target="_blank">Rust website</a></li>
|
||||
<li class="pure-menu-item"><a class="pure-menu-link" href="https://doc.rust-lang.org/book/" target="_blank">The Book</a></li>
|
||||
|
||||
<li class="pure-menu-item"><a class="pure-menu-link" href="https://doc.rust-lang.org/std/" target="_blank">Standard Library API Reference</a></li>
|
||||
|
||||
<li class="pure-menu-item"><a class="pure-menu-link" href="https://doc.rust-lang.org/rust-by-example/" target="_blank">Rust by Example</a></li>
|
||||
|
||||
<li class="pure-menu-item"><a class="pure-menu-link" href="https://doc.rust-lang.org/cargo/guide/" target="_blank">The Cargo Guide</a></li>
|
||||
|
||||
<li class="pure-menu-item"><a class="pure-menu-link" href="https://doc.rust-lang.org/nightly/clippy" target="_blank">Clippy Documentation</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div id="search-input-nav">
|
||||
<label for="nav-search">
|
||||
<span class="fa fa-solid fa-magnifying-glass " aria-hidden="true"></span>
|
||||
</label>
|
||||
|
||||
|
||||
|
||||
<input id="nav-search" name="query" type="text" aria-label="Find crate by search query" tabindex="-1" placeholder="Find crate">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div><div class="rustdoc mod crate container-rustdoc" id="rustdoc_body_wrapper" tabindex="-1"><script async="" src="/-/static/menu.js?0-1-0-7bfb09c2-2026-01-03"></script>
|
||||
<script async="" src="/-/static/index.js?0-1-0-7bfb09c2-2026-01-03"></script>
|
||||
|
||||
<iframe src="/-/storage-change-detection.html" width="0" height="0" style="display: none"></iframe><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar>
|
||||
<button class="sidebar-menu-toggle" slot="sidebar-menu-toggle" title="show sidebar">
|
||||
</button>
|
||||
<div class="settings-menu" slot="settings-menu" tabindex="-1">
|
||||
<a href="../settings.html"><span class="label">Settings</span></a>
|
||||
</div>
|
||||
<div class="help-menu" slot="help-menu" tabindex="-1">
|
||||
<a href="../help.html"><span class="label">Help</span></a>
|
||||
</div>
|
||||
<h2><a href="#">Crate tokio_stream</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../tokio_stream/index.html">tokio_<wbr>stream</a><span class="version">0.1.18</span></h2></div><div class="sidebar-elems"><ul class="block"><li><a id="all-types" href="all.html">All Items</a></li></ul><section id="rustdoc-toc"><h3><a href="#">Sections</a></h3><ul class="block top-toc"><li><a href="#iterating-over-a-stream" title="Iterating over a Stream">Iterating over a Stream</a></li><li><a href="#returning-a-stream-from-a-function" title="Returning a Stream from a function">Returning a Stream from a function</a></li><li><a href="#conversion-to-and-from-asyncreadasyncwrite" title="Conversion to and from `AsyncRead`/`AsyncWrite`">Conversion to and from <code>AsyncRead</code>/<code>AsyncWrite</code></a></li></ul><h3><a href="#reexports">Crate Items</a></h3><ul class="block"><li><a href="#reexports" title="Re-exports">Re-exports</a></li><li><a href="#modules" title="Modules">Modules</a></li><li><a href="#structs" title="Structs">Structs</a></li><li><a href="#traits" title="Traits">Traits</a></li><li><a href="#functions" title="Functions">Functions</a></li></ul></section><div id="rustdoc-modnav"><h3>Crates</h3><ul class="block crate"><li class="current"><a href="../tokio_stream/index.html">tokio_stream</a></li></ul></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content"><div class="main-heading"><div id="sidebar-button"><a href="../tokio_stream/all.html" title="show sidebar"></a></div><h1>Crate <span>tokio_<wbr>stream</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><span class="sub-heading"><a class="src" href="../src/tokio_stream/lib.rs.html#1-117">Source</a> </span><rustdoc-toolbar>
|
||||
<div id="search-button" tabindex="-1">
|
||||
<a href="https://docs.rs/tokio-stream/latest/tokio_stream/?search="><span class="label">Search</span></a>
|
||||
</div>
|
||||
<div class="settings-menu" tabindex="-1">
|
||||
<a href="../settings.html"><span class="label">Settings</span></a>
|
||||
</div>
|
||||
<div class="help-menu" tabindex="-1">
|
||||
<a href="../help.html"><span class="label">Help</span></a>
|
||||
</div>
|
||||
<button id="toggle-all-docs" title="Collapse sections (shift-click to also collapse impl blocks)"><span class="label">Summary</span></button></rustdoc-toolbar></div><details class="toggle top-doc" open=""><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Stream utilities for Tokio.</p>
|
||||
<p>A <code>Stream</code> is an asynchronous sequence of values. It can be thought of as
|
||||
an asynchronous version of the standard library’s <code>Iterator</code> trait.</p>
|
||||
<p>This crate provides helpers to work with them. For examples of usage and a more in-depth
|
||||
description of streams you can also refer to the <a href="https://tokio.rs/tokio/tutorial/streams">streams
|
||||
tutorial</a> on the tokio website.</p>
|
||||
<h2 id="iterating-over-a-stream"><a class="doc-anchor" href="#iterating-over-a-stream">§</a>Iterating over a Stream</h2>
|
||||
<p>Due to similarities with the standard library’s <code>Iterator</code> trait, some new
|
||||
users may assume that they can use <code>for in</code> syntax to iterate over a
|
||||
<code>Stream</code>, but this is unfortunately not possible. Instead, you can use a
|
||||
<code>while let</code> loop as follows:</p>
|
||||
|
||||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">use </span>tokio_stream::{<span class="self">self </span><span class="kw">as </span>stream, StreamExt};
|
||||
|
||||
<span class="kw">let </span><span class="kw-2">mut </span>stream = stream::iter(<span class="macro">vec!</span>[<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>]);
|
||||
|
||||
<span class="kw">while let </span><span class="prelude-val">Some</span>(value) = stream.next().<span class="kw">await </span>{
|
||||
<span class="macro">println!</span>(<span class="string">"Got {}"</span>, value);
|
||||
}</code></pre></div><h2 id="returning-a-stream-from-a-function"><a class="doc-anchor" href="#returning-a-stream-from-a-function">§</a>Returning a Stream from a function</h2>
|
||||
<p>A common way to stream values from a function is to pass in the sender
|
||||
half of a channel and use the receiver as the stream. This requires awaiting
|
||||
both futures to ensure progress is made. Another alternative is the
|
||||
<a href="https://docs.rs/async-stream">async-stream</a> crate, which contains macros that provide a <code>yield</code> keyword
|
||||
and allow you to return an <code>impl Stream</code>.</p>
|
||||
<h2 id="conversion-to-and-from-asyncreadasyncwrite"><a class="doc-anchor" href="#conversion-to-and-from-asyncreadasyncwrite">§</a>Conversion to and from <code>AsyncRead</code>/<code>AsyncWrite</code></h2>
|
||||
<p>It is often desirable to convert a <code>Stream</code> into an <a href="https://docs.rs/tokio/latest/tokio/io/trait.AsyncRead.html"><code>AsyncRead</code></a>,
|
||||
especially when dealing with plaintext formats streamed over the network.
|
||||
The opposite conversion from an <a href="https://docs.rs/tokio/latest/tokio/io/trait.AsyncRead.html"><code>AsyncRead</code></a> into a <code>Stream</code> is also
|
||||
another commonly required feature. To enable these conversions,
|
||||
<a href="https://docs.rs/tokio-util/latest/tokio_util/codec/index.html"><code>tokio-util</code></a> provides the <a href="https://docs.rs/tokio-util/latest/tokio_util/io/struct.StreamReader.html"><code>StreamReader</code></a> and <a href="https://docs.rs/tokio-util/latest/tokio_util/io/struct.ReaderStream.html"><code>ReaderStream</code></a>
|
||||
types when the io feature is enabled.</p>
|
||||
</div></details><h2 id="reexports" class="section-header">Re-exports<a href="#reexports" class="anchor">§</a></h2><dl class="item-table reexports"><dt id="reexport.Stream"><code>pub use futures_core::<a class="trait" href="https://docs.rs/futures-core/0.3.31/x86_64-unknown-linux-gnu/futures_core/stream/trait.Stream.html" title="trait futures_core::stream::Stream">Stream</a>;</code></dt></dl><h2 id="modules" class="section-header">Modules<a href="#modules" class="anchor">§</a></h2><dl class="item-table"><dt><a class="mod" href="adapters/index.html" title="mod tokio_stream::adapters">adapters</a></dt><dd>Adapters for <a href="https://docs.rs/futures-core/0.3.31/x86_64-unknown-linux-gnu/futures_core/stream/trait.Stream.html" title="trait futures_core::stream::Stream"><code>Stream</code></a>s created by methods in <a href="trait.StreamExt.html" title="trait tokio_stream::StreamExt"><code>StreamExt</code></a>.</dd><dt><a class="mod" href="wrappers/index.html" title="mod tokio_stream::wrappers">wrappers</a></dt><dd>Wrappers for Tokio types that implement <code>Stream</code>.</dd></dl><h2 id="structs" class="section-header">Structs<a href="#structs" class="anchor">§</a></h2><dl class="item-table"><dt><a class="struct" href="struct.Elapsed.html" title="struct tokio_stream::Elapsed">Elapsed</a><wbr><span class="stab portability" title="Available on crate feature `time` only"><code>time</code></span></dt><dd>Error returned by <code>Timeout</code> and <code>TimeoutRepeating</code>.</dd><dt><a class="struct" href="struct.Empty.html" title="struct tokio_stream::Empty">Empty</a></dt><dd>Stream for the <a href="fn.empty.html" title="fn tokio_stream::empty"><code>empty</code></a> function.</dd><dt><a class="struct" href="struct.Iter.html" title="struct tokio_stream::Iter">Iter</a></dt><dd>Stream for the <a href="fn.iter.html" title="fn tokio_stream::iter"><code>iter</code></a> function.</dd><dt><a class="struct" href="struct.Once.html" title="struct tokio_stream::Once">Once</a></dt><dd>Stream for the <a href="fn.once.html" title="fn tokio_stream::once"><code>once</code></a> function.</dd><dt><a class="struct" href="struct.Pending.html" title="struct tokio_stream::Pending">Pending</a></dt><dd>Stream for the <a href="fn.pending.html" title="fn tokio_stream::pending"><code>pending</code></a> function.</dd><dt><a class="struct" href="struct.StreamMap.html" title="struct tokio_stream::StreamMap">Stream<wbr>Map</a></dt><dd>Combine many streams into one, indexing each source stream with a unique
|
||||
key.</dd><dt><a class="struct" href="struct.StreamNotifyClose.html" title="struct tokio_stream::StreamNotifyClose">Stream<wbr>Notify<wbr>Close</a></dt><dd>A <code>Stream</code> that wraps the values in an <code>Option</code>.</dd></dl><h2 id="traits" class="section-header">Traits<a href="#traits" class="anchor">§</a></h2><dl class="item-table"><dt><a class="trait" href="trait.FromStream.html" title="trait tokio_stream::FromStream">From<wbr>Stream</a></dt><dd>Convert from a <a href="https://docs.rs/futures-core/0.3.31/x86_64-unknown-linux-gnu/futures_core/stream/trait.Stream.html" title="trait futures_core::stream::Stream"><code>Stream</code></a>.</dd><dt><a class="trait" href="trait.StreamExt.html" title="trait tokio_stream::StreamExt">Stream<wbr>Ext</a></dt><dd>An extension trait for the <a href="https://docs.rs/futures-core/0.3.31/x86_64-unknown-linux-gnu/futures_core/stream/trait.Stream.html" title="trait futures_core::stream::Stream"><code>Stream</code></a> trait that provides a variety of
|
||||
convenient combinator functions.</dd></dl><h2 id="functions" class="section-header">Functions<a href="#functions" class="anchor">§</a></h2><dl class="item-table"><dt><a class="fn" href="fn.empty.html" title="fn tokio_stream::empty">empty</a></dt><dd>Creates a stream that yields nothing.</dd><dt><a class="fn" href="fn.iter.html" title="fn tokio_stream::iter">iter</a></dt><dd>Converts an <code>Iterator</code> into a <code>Stream</code> which is always ready
|
||||
to yield the next value.</dd><dt><a class="fn" href="fn.once.html" title="fn tokio_stream::once">once</a></dt><dd>Creates a stream that emits an element exactly once.</dd><dt><a class="fn" href="fn.pending.html" title="fn tokio_stream::pending">pending</a></dt><dd>Creates a stream that is never ready</dd></dl></section><section id="alternative-display" class="content hidden"></section><section id="not-displayed" class="hidden"><section id="search"><div class="main-heading search-results-main-heading"><nav class="sub">
|
||||
<form class="search-form loading">
|
||||
<span></span> <!-- This empty span is a hacky fix for Safari: see #93184 -->
|
||||
<input class="search-input" name="search" aria-label="Run search in the documentation" autocomplete="off" spellcheck="false" placeholder="Type ‘S’ or ‘/’ to search, ‘?’ for more options…" type="search">
|
||||
</form>
|
||||
</nav><div class="search-switcher"></div></div><div class="search-out"></div></section></section></div></main></div></body></html>
|
||||
304
tmp/venturebeat_claude_code.html
Normal file
304
tmp/venturebeat_claude_code.html
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user