diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 0920de48eb87e..0ca603d3a1917 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -762,7 +762,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
         let features = match await_kind {
             FutureKind::Future => None,
-            FutureKind::AsyncIterator => Some(self.allow_for_await.clone()),
+            FutureKind::Stream => Some(self.allow_for_await.clone()),
         };
         let span = self.mark_span_with_reason(DesugaringKind::Await, await_kw_span, features);
         let gen_future_span = self.mark_span_with_reason(
@@ -816,9 +816,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     hir::LangItem::FuturePoll,
                     arena_vec![self; new_unchecked, get_context],
                 ),
-                FutureKind::AsyncIterator => self.expr_call_lang_item_fn(
+                FutureKind::Stream => self.expr_call_lang_item_fn(
                     span,
-                    hir::LangItem::AsyncIteratorPollNext,
+                    hir::LangItem::AsyncStreamPollNext,
                     arena_vec![self; new_unchecked, get_context],
                 ),
             };
@@ -910,8 +910,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 arena_vec![self; *expr],
             ),
             // Not needed for `for await` because we expect to have already called
-            // `IntoAsyncIterator::into_async_iter` on it.
-            FutureKind::AsyncIterator => expr,
+            // `IntoStream::into_stream` on it.
+            FutureKind::Stream => expr,
         };
 
         // match <into_future_expr> {
@@ -1621,7 +1621,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 }
                 ForLoopKind::ForAwait => {
                     // we'll generate `unsafe { Pin::new_unchecked(&mut iter) })` and then pass this
-                    // to make_lowered_await with `FutureKind::AsyncIterator` which will generator
+                    // to make_lowered_await with `FutureKind::Stream` which will generator
                     // calls to `poll_next`. In user code, this would probably be a call to
                     // `Pin::as_mut` but here it's easy enough to do `new_unchecked`.
 
@@ -1635,7 +1635,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     ));
                     // `unsafe { ... }`
                     let iter = self.arena.alloc(self.expr_unsafe(iter));
-                    let kind = self.make_lowered_await(head_span, iter, FutureKind::AsyncIterator);
+                    let kind = self.make_lowered_await(head_span, iter, FutureKind::Stream);
                     self.arena.alloc(hir::Expr { hir_id: self.next_id(), kind, span: head_span })
                 }
             };
@@ -1670,12 +1670,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     arena_vec![self; head],
                 )
             }
-            // ` unsafe { Pin::new_unchecked(&mut into_async_iter(<head>)) }`
+            // ` unsafe { Pin::new_unchecked(&mut into_stream(<head>)) }`
             ForLoopKind::ForAwait => {
-                // `::core::async_iter::IntoAsyncIterator::into_async_iter(<head>)`
+                // `::core::stream::IntoStream::into_stream(<head>)`
                 let iter = self.expr_call_lang_item_fn(
                     head_span,
-                    hir::LangItem::IntoAsyncIterIntoIter,
+                    hir::LangItem::IntoAsyncStreamIntoStream,
                     arena_vec![self; head],
                 );
                 let iter = self.expr_mut_addr_of(head_span, iter);
@@ -2096,7 +2096,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
 enum FutureKind {
     /// We are awaiting a normal future
     Future,
-    /// We are awaiting something that's known to be an AsyncIterator (i.e. we are in the header of
+    /// We are awaiting something that's known to be an Stream (i.e. we are in the header of
     /// a `for await` loop)
-    AsyncIterator,
+    Stream,
 }
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 057fe65d0afad..002a589b3b98d 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -131,7 +131,7 @@ struct LoweringContext<'a, 'hir> {
 
     allow_try_trait: Lrc<[Symbol]>,
     allow_gen_future: Lrc<[Symbol]>,
-    allow_async_iterator: Lrc<[Symbol]>,
+    allow_async_stream: Lrc<[Symbol]>,
     allow_for_await: Lrc<[Symbol]>,
 
     /// Mapping from generics `def_id`s to TAIT generics `def_id`s.
@@ -177,10 +177,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             } else {
                 [sym::gen_future].into()
             },
-            allow_for_await: [sym::async_iterator].into(),
+            allow_for_await: [sym::async_stream].into(),
             // FIXME(gen_blocks): how does `closure_track_caller`/`async_fn_track_caller`
             // interact with `gen`/`async gen` blocks
-            allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
+            allow_async_stream: [sym::gen_future, sym::async_stream].into(),
             generics_def_id_map: Default::default(),
             host_param_id: None,
         }
@@ -1924,7 +1924,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             CoroutineKind::Async { return_impl_trait_id, .. } => (return_impl_trait_id, None),
             CoroutineKind::Gen { return_impl_trait_id, .. } => (return_impl_trait_id, None),
             CoroutineKind::AsyncGen { return_impl_trait_id, .. } => {
-                (return_impl_trait_id, Some(self.allow_async_iterator.clone()))
+                (return_impl_trait_id, Some(self.allow_async_stream.clone()))
             }
         };
 
@@ -1986,7 +1986,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         let (assoc_ty_name, trait_lang_item) = match coro {
             CoroutineKind::Async { .. } => (sym::Output, hir::LangItem::Future),
             CoroutineKind::Gen { .. } => (sym::Item, hir::LangItem::Iterator),
-            CoroutineKind::AsyncGen { .. } => (sym::Item, hir::LangItem::AsyncIterator),
+            CoroutineKind::AsyncGen { .. } => (sym::Item, hir::LangItem::AsyncStream),
         };
 
         let bound_args = self.arena.alloc(hir::GenericArgs {
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 1cc1f11b3c858..7972ffa25d3f0 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -212,7 +212,7 @@ language_item_table! {
 
     Iterator,                sym::iterator,            iterator_trait,             Target::Trait,          GenericRequirement::Exact(0);
     Future,                  sym::future_trait,        future_trait,               Target::Trait,          GenericRequirement::Exact(0);
-    AsyncIterator,           sym::async_iterator,      async_iterator_trait,       Target::Trait,          GenericRequirement::Exact(0);
+    AsyncStream,             sym::async_stream,        async_stream_trait,         Target::Trait,          GenericRequirement::Exact(0);
     CoroutineState,          sym::coroutine_state,     coroutine_state,            Target::Enum,           GenericRequirement::None;
     Coroutine,               sym::coroutine,           coroutine_trait,            Target::Trait,          GenericRequirement::Minimum(1);
     Unpin,                   sym::unpin,               unpin_trait,                Target::Trait,          GenericRequirement::None;
@@ -307,8 +307,8 @@ language_item_table! {
     Context,                 sym::Context,             context,                    Target::Struct,         GenericRequirement::None;
     FuturePoll,              sym::poll,                future_poll_fn,             Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
 
-    AsyncIteratorPollNext,   sym::async_iterator_poll_next, async_iterator_poll_next, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::Exact(0);
-    IntoAsyncIterIntoIter,   sym::into_async_iter_into_iter, into_async_iter_into_iter, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::Exact(0);
+    AsyncStreamPollNext,     sym::async_stream_poll_next, async_stream_poll_next, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::Exact(0);
+    IntoAsyncStreamIntoStream, sym::into_async_stream_into_stream, into_async_stream_into_stream, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::Exact(0);
 
     Option,                  sym::Option,              option_type,                Target::Enum,           GenericRequirement::None;
     OptionSome,              sym::Some,                option_some_variant,        Target::Variant,        GenericRequirement::None;
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 7b0138d50baed..1687931121303 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -429,8 +429,8 @@ symbols! {
         async_fn_in_trait,
         async_fn_track_caller,
         async_for_loop,
-        async_iterator,
-        async_iterator_poll_next,
+        async_stream,
+        async_stream_poll_next,
         atomic,
         atomic_mod,
         atomics,
@@ -899,7 +899,7 @@ symbols! {
         instruction_set,
         integer_: "integer", // underscore to avoid clashing with the function `sym::integer` below
         integral,
-        into_async_iter_into_iter,
+        into_async_stream_into_stream,
         into_future,
         into_iter,
         intra_doc_pointers,
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index caf9470b4c646..5fb133390aac9 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -214,7 +214,7 @@ pub(super) trait GoalKind<'tcx>:
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx>;
 
-    fn consider_builtin_async_iterator_candidate(
+    fn consider_builtin_async_stream_candidate(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx>;
@@ -577,8 +577,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             G::consider_builtin_future_candidate(self, goal)
         } else if lang_items.iterator_trait() == Some(trait_def_id) {
             G::consider_builtin_iterator_candidate(self, goal)
-        } else if lang_items.async_iterator_trait() == Some(trait_def_id) {
-            G::consider_builtin_async_iterator_candidate(self, goal)
+        } else if lang_items.async_stream_trait() == Some(trait_def_id) {
+            G::consider_builtin_async_stream_candidate(self, goal)
         } else if lang_items.coroutine_trait() == Some(trait_def_id) {
             G::consider_builtin_coroutine_candidate(self, goal)
         } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) {
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
index ccee6f8eb29b9..135b316fb73d6 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
@@ -525,7 +525,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
         )
     }
 
-    fn consider_builtin_async_iterator_candidate(
+    fn consider_builtin_async_stream_candidate(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx> {
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index be07927568446..b26fbdee33e78 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -377,7 +377,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
     }
 
-    fn consider_builtin_async_iterator_candidate(
+    fn consider_builtin_async_stream_candidate(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx> {
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index abbc2066eac16..321764674421e 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -1829,7 +1829,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                     lang_items.coroutine_trait(),
                     lang_items.future_trait(),
                     lang_items.iterator_trait(),
-                    lang_items.async_iterator_trait(),
+                    lang_items.async_stream_trait(),
                     lang_items.fn_trait(),
                     lang_items.fn_mut_trait(),
                     lang_items.fn_once_trait(),
@@ -2051,8 +2051,8 @@ fn confirm_select_candidate<'cx, 'tcx>(
                 confirm_future_candidate(selcx, obligation, data)
             } else if lang_items.iterator_trait() == Some(trait_def_id) {
                 confirm_iterator_candidate(selcx, obligation, data)
-            } else if lang_items.async_iterator_trait() == Some(trait_def_id) {
-                confirm_async_iterator_candidate(selcx, obligation, data)
+            } else if lang_items.async_stream_trait() == Some(trait_def_id) {
+                confirm_async_stream_candidate(selcx, obligation, data)
             } else if selcx.tcx().fn_trait_kind_from_def_id(trait_def_id).is_some() {
                 if obligation.predicate.self_ty().is_closure() {
                     confirm_closure_candidate(selcx, obligation, data)
@@ -2218,7 +2218,7 @@ fn confirm_iterator_candidate<'cx, 'tcx>(
         .with_addl_obligations(obligations)
 }
 
-fn confirm_async_iterator_candidate<'cx, 'tcx>(
+fn confirm_async_stream_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
     nested: Vec<PredicateObligation<'tcx>>,
@@ -2236,12 +2236,12 @@ fn confirm_async_iterator_candidate<'cx, 'tcx>(
         gen_sig,
     );
 
-    debug!(?obligation, ?gen_sig, ?obligations, "confirm_async_iterator_candidate");
+    debug!(?obligation, ?gen_sig, ?obligations, "confirm_async_stream_candidate");
 
     let tcx = selcx.tcx();
-    let iter_def_id = tcx.require_lang_item(LangItem::AsyncIterator, None);
+    let iter_def_id = tcx.require_lang_item(LangItem::AsyncStream, None);
 
-    let (trait_ref, yield_ty) = super::util::async_iterator_trait_ref_and_outputs(
+    let (trait_ref, yield_ty) = super::util::async_stream_trait_ref_and_outputs(
         tcx,
         iter_def_id,
         obligation.predicate.self_ty(),
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index 342b12ba49848..27666e9046b7e 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -112,8 +112,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     self.assemble_future_candidates(obligation, &mut candidates);
                 } else if lang_items.iterator_trait() == Some(def_id) {
                     self.assemble_iterator_candidates(obligation, &mut candidates);
-                } else if lang_items.async_iterator_trait() == Some(def_id) {
-                    self.assemble_async_iterator_candidates(obligation, &mut candidates);
+                } else if lang_items.async_stream_trait() == Some(def_id) {
+                    self.assemble_async_stream_candidates(obligation, &mut candidates);
                 }
 
                 self.assemble_closure_candidates(obligation, &mut candidates);
@@ -257,7 +257,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
     }
 
-    fn assemble_async_iterator_candidates(
+    fn assemble_async_stream_candidates(
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
         candidates: &mut SelectionCandidateSet<'tcx>,
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 74f388e53a3c9..d95eacfc45b3e 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -99,7 +99,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             }
 
             AsyncIteratorCandidate => {
-                let vtable_iterator = self.confirm_async_iterator_candidate(obligation)?;
+                let vtable_iterator = self.confirm_async_stream_candidate(obligation)?;
                 ImplSource::Builtin(BuiltinImplSource::Misc, vtable_iterator)
             }
 
@@ -818,7 +818,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         Ok(nested)
     }
 
-    fn confirm_async_iterator_candidate(
+    fn confirm_async_stream_candidate(
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
     ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
@@ -830,11 +830,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             bug!("closure candidate for non-closure {:?}", obligation);
         };
 
-        debug!(?obligation, ?coroutine_def_id, ?args, "confirm_async_iterator_candidate");
+        debug!(?obligation, ?coroutine_def_id, ?args, "confirm_async_stream_candidate");
 
         let gen_sig = args.as_coroutine().sig();
 
-        let (trait_ref, _) = super::util::async_iterator_trait_ref_and_outputs(
+        let (trait_ref, _) = super::util::async_stream_trait_ref_and_outputs(
             self.tcx(),
             obligation.predicate.def_id(),
             obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(),
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index c40ed10e52ff3..b20ebed367fcf 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -320,14 +320,14 @@ pub fn iterator_trait_ref_and_outputs<'tcx>(
     (trait_ref, sig.yield_ty)
 }
 
-pub fn async_iterator_trait_ref_and_outputs<'tcx>(
+pub fn async_stream_trait_ref_and_outputs<'tcx>(
     tcx: TyCtxt<'tcx>,
-    async_iterator_def_id: DefId,
+    async_stream_def_id: DefId,
     self_ty: Ty<'tcx>,
     sig: ty::GenSig<'tcx>,
 ) -> (ty::TraitRef<'tcx>, Ty<'tcx>) {
     assert!(!self_ty.has_escaping_bound_vars());
-    let trait_ref = ty::TraitRef::new(tcx, async_iterator_def_id, [self_ty]);
+    let trait_ref = ty::TraitRef::new(tcx, async_stream_def_id, [self_ty]);
     (trait_ref, sig.yield_ty)
 }
 
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index 81d5304b81265..de3534f9d0782 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -271,7 +271,7 @@ fn resolve_associated_item<'tcx>(
                     debug_assert!(tcx.defaultness(trait_item_id).has_value());
                     Some(Instance::new(trait_item_id, rcvr_args))
                 }
-            } else if Some(trait_ref.def_id) == lang_items.async_iterator_trait() {
+            } else if Some(trait_ref.def_id) == lang_items.async_stream_trait() {
                 let ty::Coroutine(coroutine_def_id, args) = *rcvr_args.type_at(0).kind() else {
                     bug!()
                 };
diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs
index 6977681e5a397..a1db87659366e 100644
--- a/library/alloc/src/boxed.rs
+++ b/library/alloc/src/boxed.rs
@@ -164,6 +164,7 @@ use core::ops::{
 };
 use core::pin::Pin;
 use core::ptr::{self, NonNull, Unique};
+use core::stream::Stream;
 use core::task::{Context, Poll};
 
 #[cfg(not(no_global_oom_handling))]
@@ -2153,8 +2154,8 @@ where
     }
 }
 
-#[unstable(feature = "async_iterator", issue = "79024")]
-impl<S: ?Sized + AsyncIterator + Unpin> AsyncIterator for Box<S> {
+#[unstable(feature = "async_stream", issue = "79024")]
+impl<S: ?Sized + Stream + Unpin> Stream for Box<S> {
     type Item = S::Item;
 
     fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
@@ -2166,6 +2167,19 @@ impl<S: ?Sized + AsyncIterator + Unpin> AsyncIterator for Box<S> {
     }
 }
 
+#[unstable(feature = "async_iterator", issue = "79024")]
+impl<I: ?Sized + AsyncIterator> AsyncIterator for Box<I> {
+    type Item = I::Item;
+
+    async fn next(&mut self) -> Option<Self::Item> {
+        (&mut **self).next().await
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (**self).size_hint()
+    }
+}
+
 impl dyn Error {
     #[inline]
     #[stable(feature = "error_downcast", since = "1.3.0")]
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index 78629b39d34b1..e6120d3bcd7c0 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -108,6 +108,7 @@
 #![feature(ascii_char)]
 #![feature(assert_matches)]
 #![feature(async_iterator)]
+#![feature(async_stream)]
 #![feature(coerce_unsized)]
 #![feature(const_align_of_val)]
 #![feature(const_box)]
diff --git a/library/core/src/async_iter/async_iter.rs b/library/core/src/async_iter/async_iter.rs
index db71a286b6dd0..de3d8e0d0e194 100644
--- a/library/core/src/async_iter/async_iter.rs
+++ b/library/core/src/async_iter/async_iter.rs
@@ -1,19 +1,19 @@
-use crate::ops::DerefMut;
-use crate::pin::Pin;
-use crate::task::{Context, Poll};
-
 /// A trait for dealing with asynchronous iterators.
 ///
+/// This trait is an alternative to the [`Stream`] trait. Both traits are
+/// currently being evaluated on nightly with the intent to stabilize only one.
+///
 /// This is the main async iterator trait. For more about the concept of async iterators
 /// generally, please see the [module-level documentation]. In particular, you
 /// may want to know how to [implement `AsyncIterator`][impl].
 ///
 /// [module-level documentation]: index.html
 /// [impl]: index.html#implementing-async-iterator
+/// [`Stream`]: crate::stream::Stream
 #[unstable(feature = "async_iterator", issue = "79024")]
 #[must_use = "async iterators do nothing unless polled"]
 #[doc(alias = "Stream")]
-#[lang = "async_iterator"]
+#[allow(async_fn_in_trait)]
 pub trait AsyncIterator {
     /// The type of items yielded by the async iterator.
     type Item;
@@ -21,34 +21,7 @@ pub trait AsyncIterator {
     /// Attempt to pull out the next value of this async iterator, registering the
     /// current task for wakeup if the value is not yet available, and returning
     /// `None` if the async iterator is exhausted.
-    ///
-    /// # Return value
-    ///
-    /// There are several possible return values, each indicating a distinct
-    /// async iterator state:
-    ///
-    /// - `Poll::Pending` means that this async iterator's next value is not ready
-    /// yet. Implementations will ensure that the current task will be notified
-    /// when the next value may be ready.
-    ///
-    /// - `Poll::Ready(Some(val))` means that the async iterator has successfully
-    /// produced a value, `val`, and may produce further values on subsequent
-    /// `poll_next` calls.
-    ///
-    /// - `Poll::Ready(None)` means that the async iterator has terminated, and
-    /// `poll_next` should not be invoked again.
-    ///
-    /// # Panics
-    ///
-    /// Once an async iterator has finished (returned `Ready(None)` from `poll_next`), calling its
-    /// `poll_next` method again may panic, block forever, or cause other kinds of
-    /// problems; the `AsyncIterator` trait places no requirements on the effects of
-    /// such a call. However, as the `poll_next` method is not marked `unsafe`,
-    /// Rust's usual rules apply: calls must never cause undefined behavior
-    /// (memory corruption, incorrect use of `unsafe` functions, or the like),
-    /// regardless of the async iterator's state.
-    #[cfg_attr(not(bootstrap), lang = "async_iterator_poll_next")]
-    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>;
+    async fn next(&mut self) -> Option<Self::Item>;
 
     /// Returns the bounds on the remaining length of the async iterator.
     ///
@@ -83,28 +56,11 @@ pub trait AsyncIterator {
 }
 
 #[unstable(feature = "async_iterator", issue = "79024")]
-impl<S: ?Sized + AsyncIterator + Unpin> AsyncIterator for &mut S {
-    type Item = S::Item;
-
-    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
-        S::poll_next(Pin::new(&mut **self), cx)
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        (**self).size_hint()
-    }
-}
-
-#[unstable(feature = "async_iterator", issue = "79024")]
-impl<P> AsyncIterator for Pin<P>
-where
-    P: DerefMut,
-    P::Target: AsyncIterator,
-{
-    type Item = <P::Target as AsyncIterator>::Item;
+impl<I: ?Sized + AsyncIterator> AsyncIterator for &mut I {
+    type Item = I::Item;
 
-    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
-        <P::Target as AsyncIterator>::poll_next(self.as_deref_mut(), cx)
+    async fn next(&mut self) -> Option<Self::Item> {
+        (**self).next().await
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
@@ -112,30 +68,6 @@ where
     }
 }
 
-#[unstable(feature = "async_gen_internals", issue = "none")]
-impl<T> Poll<Option<T>> {
-    /// A helper function for internal desugaring -- produces `Ready(Some(t))`,
-    /// which corresponds to the async iterator yielding a value.
-    #[unstable(feature = "async_gen_internals", issue = "none")]
-    #[lang = "AsyncGenReady"]
-    pub fn async_gen_ready(t: T) -> Self {
-        Poll::Ready(Some(t))
-    }
-
-    /// A helper constant for internal desugaring -- produces `Pending`,
-    /// which corresponds to the async iterator pending on an `.await`.
-    #[unstable(feature = "async_gen_internals", issue = "none")]
-    #[lang = "AsyncGenPending"]
-    // FIXME(gen_blocks): This probably could be deduplicated.
-    pub const PENDING: Self = Poll::Pending;
-
-    /// A helper constant for internal desugaring -- produces `Ready(None)`,
-    /// which corresponds to the async iterator finishing its iteration.
-    #[unstable(feature = "async_gen_internals", issue = "none")]
-    #[lang = "AsyncGenFinished"]
-    pub const FINISHED: Self = Poll::Ready(None);
-}
-
 /// Convert something into an async iterator
 #[unstable(feature = "async_iterator", issue = "79024")]
 pub trait IntoAsyncIterator {
@@ -145,7 +77,6 @@ pub trait IntoAsyncIterator {
     type IntoAsyncIter: AsyncIterator<Item = Self::Item>;
 
     /// Converts `self` into an async iterator
-    #[cfg_attr(not(bootstrap), lang = "into_async_iter_into_iter")]
     fn into_async_iter(self) -> Self::IntoAsyncIter;
 }
 
diff --git a/library/core/src/async_iter/from_iter.rs b/library/core/src/async_iter/from_iter.rs
index 3180187afc8c9..4573c8e804812 100644
--- a/library/core/src/async_iter/from_iter.rs
+++ b/library/core/src/async_iter/from_iter.rs
@@ -1,7 +1,4 @@
-use crate::pin::Pin;
-
 use crate::async_iter::AsyncIterator;
-use crate::task::{Context, Poll};
 
 /// An async iterator that was created from iterator.
 ///
@@ -28,8 +25,8 @@ pub fn from_iter<I: IntoIterator>(iter: I) -> FromIter<I::IntoIter> {
 impl<I: Iterator> AsyncIterator for FromIter<I> {
     type Item = I::Item;
 
-    fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
-        Poll::Ready(self.iter.next())
+    async fn next(&mut self) -> Option<Self::Item> {
+        self.iter.next()
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
diff --git a/library/core/src/async_iter/mod.rs b/library/core/src/async_iter/mod.rs
index e1f1c9075823d..ad51084ac315a 100644
--- a/library/core/src/async_iter/mod.rs
+++ b/library/core/src/async_iter/mod.rs
@@ -30,20 +30,12 @@
 //! [`AsyncIterator`] looks like this:
 //!
 //! ```
-//! # use core::task::{Context, Poll};
-//! # use core::pin::Pin;
 //! trait AsyncIterator {
 //!     type Item;
-//!     fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>;
+//!     async fn next(&mut self) -> Option<Self::Item>;
 //! }
 //! ```
 //!
-//! Unlike `Iterator`, `AsyncIterator` makes a distinction between the [`poll_next`]
-//! method which is used when implementing an `AsyncIterator`, and a (to-be-implemented)
-//! `next` method which is used when consuming an async iterator. Consumers of `AsyncIterator`
-//! only need to consider `next`, which when called, returns a future which
-//! yields `Option<AsyncIterator::Item>`.
-//!
 //! The future returned by `next` will yield `Some(Item)` as long as there are
 //! elements, and once they've all been exhausted, will yield `None` to indicate
 //! that iteration is finished. If we're waiting on something asynchronous to
@@ -53,11 +45,10 @@
 //! again may or may not eventually yield `Some(Item)` again at some point.
 //!
 //! [`AsyncIterator`]'s full definition includes a number of other methods as well,
-//! but they are default methods, built on top of [`poll_next`], and so you get
+//! but they are default methods, built on top of [`next`], and so you get
 //! them for free.
 //!
-//! [`Poll`]: super::task::Poll
-//! [`poll_next`]: AsyncIterator::poll_next
+//! [`next`]: AsyncIterator::next
 //!
 //! # Implementing Async Iterator
 //!
@@ -70,8 +61,6 @@
 //! ```no_run
 //! #![feature(async_iterator)]
 //! # use core::async_iter::AsyncIterator;
-//! # use core::task::{Context, Poll};
-//! # use core::pin::Pin;
 //!
 //! // First, the struct:
 //!
@@ -82,7 +71,7 @@
 //!
 //! // we want our count to start at one, so let's add a new() method to help.
 //! // This isn't strictly necessary, but is convenient. Note that we start
-//! // `count` at zero, we'll see why in `poll_next()`'s implementation below.
+//! // `count` at zero, we'll see why in `next()`'s implementation below.
 //! impl Counter {
 //!     fn new() -> Counter {
 //!         Counter { count: 0 }
@@ -95,16 +84,16 @@
 //!     // we will be counting with usize
 //!     type Item = usize;
 //!
-//!     // poll_next() is the only required method
-//!     fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+//!     // next() is the only required method
+//!     async fn next(&mut self) -> Option<Self::Item> {
 //!         // Increment our count. This is why we started at zero.
 //!         self.count += 1;
 //!
 //!         // Check to see if we've finished counting or not.
 //!         if self.count < 6 {
-//!             Poll::Ready(Some(self.count))
+//!             Some(self.count)
 //!         } else {
-//!             Poll::Ready(None)
+//!             None
 //!         }
 //!     }
 //! }
@@ -113,7 +102,7 @@
 //! # Laziness
 //!
 //! Async iterators are *lazy*. This means that just creating an async iterator doesn't
-//! _do_ a whole lot. Nothing really happens until you call `poll_next`. This is
+//! _do_ a whole lot. Nothing really happens until you call `next`. This is
 //! sometimes a source of confusion when creating an async iterator solely for its side
 //! effects. The compiler will warn us about this kind of behavior:
 //!
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index 24ad78efa83b1..4210e4742bfd2 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -382,6 +382,8 @@ pub mod panic;
 pub mod panicking;
 pub mod pin;
 pub mod result;
+#[unstable(feature = "async_stream", issue = "79024")]
+pub mod stream;
 pub mod sync;
 
 pub mod fmt;
diff --git a/library/core/src/panic/unwind_safe.rs b/library/core/src/panic/unwind_safe.rs
index 37859212c0ee3..39cb7092e98f1 100644
--- a/library/core/src/panic/unwind_safe.rs
+++ b/library/core/src/panic/unwind_safe.rs
@@ -5,6 +5,7 @@ use crate::future::Future;
 use crate::ops::{Deref, DerefMut};
 use crate::pin::Pin;
 use crate::ptr::{NonNull, Unique};
+use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
 /// A marker trait which represents "panic safe" types in Rust.
@@ -298,8 +299,8 @@ impl<F: Future> Future for AssertUnwindSafe<F> {
     }
 }
 
-#[unstable(feature = "async_iterator", issue = "79024")]
-impl<S: AsyncIterator> AsyncIterator for AssertUnwindSafe<S> {
+#[unstable(feature = "async_stream", issue = "79024")]
+impl<S: Stream> Stream for AssertUnwindSafe<S> {
     type Item = S::Item;
 
     fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<S::Item>> {
@@ -311,3 +312,16 @@ impl<S: AsyncIterator> AsyncIterator for AssertUnwindSafe<S> {
         self.0.size_hint()
     }
 }
+
+#[unstable(feature = "async_iterator", issue = "79024")]
+impl<I: AsyncIterator> AsyncIterator for AssertUnwindSafe<I> {
+    type Item = I::Item;
+
+    async fn next(&mut self) -> Option<I::Item> {
+        self.0.next().await
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.0.size_hint()
+    }
+}
diff --git a/library/core/src/stream/from_iter.rs b/library/core/src/stream/from_iter.rs
new file mode 100644
index 0000000000000..e435b3e788432
--- /dev/null
+++ b/library/core/src/stream/from_iter.rs
@@ -0,0 +1,38 @@
+use crate::pin::Pin;
+
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+/// A stream that was created from an iterator.
+///
+/// This stream is created by the [`from_iter`] function.
+/// See it documentation for more.
+///
+/// [`from_iter`]: fn.from_iter.html
+#[unstable(feature = "async_stream_from_iter", issue = "81798")]
+#[derive(Clone, Debug)]
+pub struct FromIter<I> {
+    iter: I,
+}
+
+#[unstable(feature = "async_stream_from_iter", issue = "81798")]
+impl<I> Unpin for FromIter<I> {}
+
+/// Converts an iterator into an async iterator.
+#[unstable(feature = "async_stream_from_iter", issue = "81798")]
+pub fn from_iter<I: IntoIterator>(iter: I) -> FromIter<I::IntoIter> {
+    FromIter { iter: iter.into_iter() }
+}
+
+#[unstable(feature = "async_stream_from_iter", issue = "81798")]
+impl<I: Iterator> Stream for FromIter<I> {
+    type Item = I::Item;
+
+    fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        Poll::Ready(self.iter.next())
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.iter.size_hint()
+    }
+}
diff --git a/library/core/src/stream/mod.rs b/library/core/src/stream/mod.rs
new file mode 100644
index 0000000000000..9c1df290df7e9
--- /dev/null
+++ b/library/core/src/stream/mod.rs
@@ -0,0 +1,128 @@
+//! Composable asynchronous iteration.
+//!
+//! If you've found yourself with an asynchronous collection of some kind,
+//! and needed to perform an operation on the elements of said collection,
+//! you'll quickly run into 'streams'. streams are heavily used in
+//! idiomatic asynchronous Rust code, so it's worth becoming familiar with them.
+//!
+//! Before explaining more, let's talk about how this module is structured:
+//!
+//! # Organization
+//!
+//! This module is largely organized by type:
+//!
+//! * [Traits] are the core portion: these traits define what kind of streams
+//!   exist and what you can do with them. The methods of these traits are worth
+//!   putting some extra study time into.
+//! * Functions provide some helpful ways to create some basic streams.
+//! * Structs are often the return types of the various methods on this
+//!   module's traits. You'll usually want to look at the method that creates
+//!   the `struct`, rather than the `struct` itself. For more detail about why,
+//!   see '[Implementing Stream](#implementing-stream)'.
+//!
+//! [Traits]: #traits
+//!
+//! That's it! Let's dig into streams.
+//!
+//! # Streams
+//!
+//! The heart and soul of this module is the [`Stream`] trait. The core of
+//! [`Stream`] looks like this:
+//!
+//! ```
+//! # use core::task::{Context, Poll};
+//! # use core::pin::Pin;
+//! trait Stream {
+//!     type Item;
+//!     fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>;
+//! }
+//! ```
+//!
+//! Unlike `Iterator`, `Stream` makes a distinction between the [`poll_next`]
+//! method which is used when implementing an `Stream`, and a (to-be-implemented)
+//! `next` method which is used when consuming an stream. Consumers of `Stream`
+//! only need to consider `next`, which when called, returns a future which
+//! yields `Option<Stream::Item>`.
+//!
+//! The future returned by `next` will yield `Some(Item)` as long as there are
+//! elements, and once they've all been exhausted, will yield `None` to indicate
+//! that iteration is finished. If we're waiting on something asynchronous to
+//! resolve, the future will wait until the stream is ready to yield again.
+//!
+//! Individual streams may choose to resume iteration, and so calling `next`
+//! again may or may not eventually yield `Some(Item)` again at some point.
+//!
+//! [`Stream`]'s full definition includes a number of other methods as well,
+//! but they are default methods, built on top of [`poll_next`], and so you get
+//! them for free.
+//!
+//! [`Poll`]: super::task::Poll
+//! [`poll_next`]: Stream::poll_next
+//!
+//! # Implementing Stream
+//!
+//! Creating an stream of your own involves two steps: creating a `struct` to
+//! hold the stream's state, and then implementing [`Stream`] for that
+//! `struct`.
+//!
+//! Let's make an stream named `Counter` which counts from `1` to `5`:
+//!
+//! ```no_run
+//! #![feature(async_stream)]
+//! # use core::stream::Stream;
+//! # use core::task::{Context, Poll};
+//! # use core::pin::Pin;
+//!
+//! // First, the struct:
+//!
+//! /// An stream which counts from one to five
+//! struct Counter {
+//!     count: usize,
+//! }
+//!
+//! // we want our count to start at one, so let's add a new() method to help.
+//! // This isn't strictly necessary, but is convenient. Note that we start
+//! // `count` at zero, we'll see why in `poll_next()`'s implementation below.
+//! impl Counter {
+//!     fn new() -> Counter {
+//!         Counter { count: 0 }
+//!     }
+//! }
+//!
+//! // Then, we implement `Stream` for our `Counter`:
+//!
+//! impl Stream for Counter {
+//!     // we will be counting with usize
+//!     type Item = usize;
+//!
+//!     // poll_next() is the only required method
+//!     fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+//!         // Increment our count. This is why we started at zero.
+//!         self.count += 1;
+//!
+//!         // Check to see if we've finished counting or not.
+//!         if self.count < 6 {
+//!             Poll::Ready(Some(self.count))
+//!         } else {
+//!             Poll::Ready(None)
+//!         }
+//!     }
+//! }
+//! ```
+//!
+//! # Laziness
+//!
+//! Streams are *lazy*. This means that just creating an stream doesn't
+//! _do_ a whole lot. Nothing really happens until you call `poll_next`. This is
+//! sometimes a source of confusion when creating an stream solely for its side
+//! effects. The compiler will warn us about this kind of behavior:
+//!
+//! ```text
+//! warning: unused result that must be used: streams do nothing unless polled
+//! ```
+
+mod from_iter;
+mod stream;
+
+pub use from_iter::{from_iter, FromIter};
+pub use stream::{IntoStream, Stream};
diff --git a/library/core/src/stream/stream.rs b/library/core/src/stream/stream.rs
new file mode 100644
index 0000000000000..e22e102015fb5
--- /dev/null
+++ b/library/core/src/stream/stream.rs
@@ -0,0 +1,163 @@
+use crate::ops::DerefMut;
+use crate::pin::Pin;
+use crate::task::{Context, Poll};
+
+/// A trait for dealing with asynchronous streams.
+///
+/// This trait is an alternative to the [`AsyncIterator`] trait. Both traits are
+/// currently being evaluated on nightly with the intent to stabilize only one.
+///
+/// This is the main stream trait. For more about the concept of streams
+/// generally, please see the [module-level documentation]. In particular, you
+/// may want to know how to [implement `Stream`][impl].
+///
+/// [module-level documentation]: index.html
+/// [impl]: index.html#implementing-stream
+/// [`AsyncIterator`]: crate::async_iter::AsyncIterator
+#[unstable(feature = "async_stream", issue = "79024")]
+#[must_use = "streams do nothing unless polled"]
+#[cfg_attr(not(bootstrap), lang = "async_stream")]
+pub trait Stream {
+    /// The type of items yielded by the stream.
+    type Item;
+
+    /// Attempt to pull out the next value of this stream, registering the
+    /// current task for wakeup if the value is not yet available, and returning
+    /// `None` if the stream is exhausted.
+    ///
+    /// # Return value
+    ///
+    /// There are several possible return values, each indicating a distinct
+    /// stream state:
+    ///
+    /// - `Poll::Pending` means that this stream's next value is not ready
+    /// yet. Implementations will ensure that the current task will be notified
+    /// when the next value may be ready.
+    ///
+    /// - `Poll::Ready(Some(val))` means that the stream has successfully
+    /// produced a value, `val`, and may produce further values on subsequent
+    /// `poll_next` calls.
+    ///
+    /// - `Poll::Ready(None)` means that the stream has terminated, and
+    /// `poll_next` should not be invoked again.
+    ///
+    /// # Panics
+    ///
+    /// Once an stream has finished (returned `Ready(None)` from `poll_next`), calling its
+    /// `poll_next` method again may panic, block forever, or cause other kinds of
+    /// problems; the `Asyncstream` trait places no requirements on the effects of
+    /// such a call. However, as the `poll_next` method is not marked `unsafe`,
+    /// Rust's usual rules apply: calls must never cause undefined behavior
+    /// (memory corruption, incorrect use of `unsafe` functions, or the like),
+    /// regardless of the stream's state.
+    #[cfg_attr(not(bootstrap), lang = "async_stream_poll_next")]
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>;
+
+    /// Returns the bounds on the remaining length of the stream.
+    ///
+    /// Specifically, `size_hint()` returns a tuple where the first element
+    /// is the lower bound, and the second element is the upper bound.
+    ///
+    /// The second half of the tuple that is returned is an <code>[Option]<[usize]></code>.
+    /// A [`None`] here means that either there is no known upper bound, or the
+    /// upper bound is larger than [`usize`].
+    ///
+    /// # Implementation notes
+    ///
+    /// It is not enforced that an stream implementation yields the declared
+    /// number of elements. A buggy stream may yield less than the lower bound
+    /// or more than the upper bound of elements.
+    ///
+    /// `size_hint()` is primarily intended to be used for optimizations such as
+    /// reserving space for the elements of the stream, but must not be
+    /// trusted to e.g., omit bounds checks in unsafe code. An incorrect
+    /// implementation of `size_hint()` should not lead to memory safety
+    /// violations.
+    ///
+    /// That said, the implementation should provide a correct estimation,
+    /// because otherwise it would be a violation of the trait's protocol.
+    ///
+    /// The default implementation returns <code>(0, [None])</code> which is correct for any
+    /// stream.
+    #[inline]
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (0, None)
+    }
+}
+
+#[unstable(feature = "async_stream", issue = "79024")]
+impl<S: ?Sized + Stream + Unpin> Stream for &mut S {
+    type Item = S::Item;
+
+    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        S::poll_next(Pin::new(&mut **self), cx)
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (**self).size_hint()
+    }
+}
+
+#[unstable(feature = "async_stream", issue = "79024")]
+impl<P> Stream for Pin<P>
+where
+    P: DerefMut,
+    P::Target: Stream,
+{
+    type Item = <P::Target as Stream>::Item;
+
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        <P::Target as Stream>::poll_next(self.as_deref_mut(), cx)
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (**self).size_hint()
+    }
+}
+
+#[unstable(feature = "async_gen_internals", issue = "none")]
+impl<T> Poll<Option<T>> {
+    /// A helper function for internal desugaring -- produces `Ready(Some(t))`,
+    /// which corresponds to the stream yielding a value.
+    #[unstable(feature = "async_gen_internals", issue = "none")]
+    #[lang = "AsyncGenReady"]
+    pub fn async_gen_ready(t: T) -> Self {
+        Poll::Ready(Some(t))
+    }
+
+    /// A helper constant for internal desugaring -- produces `Pending`,
+    /// which corresponds to the stream pending on an `.await`.
+    #[unstable(feature = "async_gen_internals", issue = "none")]
+    #[lang = "AsyncGenPending"]
+    // FIXME(gen_blocks): This probably could be deduplicated.
+    pub const PENDING: Self = Poll::Pending;
+
+    /// A helper constant for internal desugaring -- produces `Ready(None)`,
+    /// which corresponds to the stream finishing its iteration.
+    #[unstable(feature = "async_gen_internals", issue = "none")]
+    #[lang = "AsyncGenFinished"]
+    pub const FINISHED: Self = Poll::Ready(None);
+}
+
+/// Convert something into an stream
+#[unstable(feature = "async_stream", issue = "79024")]
+pub trait IntoStream {
+    /// The type of the item yielded by the stream
+    type Item;
+    /// The type of the resulting stream
+    type IntoStream: Stream<Item = Self::Item>;
+
+    /// Converts `self` into an stream
+    #[cfg_attr(not(bootstrap), lang = "into_async_stream_into_stream")]
+    fn into_stream(self) -> Self::IntoStream;
+}
+
+#[unstable(feature = "async_stream", issue = "79024")]
+impl<I: Stream> IntoStream for I {
+    type Item = I::Item;
+    type IntoStream = I;
+
+    fn into_stream(self) -> Self::IntoStream {
+        self
+    }
+}
diff --git a/library/core/tests/async_iter/mod.rs b/library/core/tests/async_iter/mod.rs
index 4f425d7286d09..296f74982d97c 100644
--- a/library/core/tests/async_iter/mod.rs
+++ b/library/core/tests/async_iter/mod.rs
@@ -1,16 +1,17 @@
 use core::async_iter::{self, AsyncIterator, IntoAsyncIterator};
+use core::future::Future;
 use core::pin::pin;
 use core::task::Poll;
 
 #[test]
 fn into_async_iter() {
     let async_iter = async_iter::from_iter(0..3);
-    let mut async_iter = pin!(async_iter.into_async_iter());
+    let mut async_iter = async_iter.into_async_iter();
 
     let mut cx = &mut core::task::Context::from_waker(core::task::Waker::noop());
 
-    assert_eq!(async_iter.as_mut().poll_next(&mut cx), Poll::Ready(Some(0)));
-    assert_eq!(async_iter.as_mut().poll_next(&mut cx), Poll::Ready(Some(1)));
-    assert_eq!(async_iter.as_mut().poll_next(&mut cx), Poll::Ready(Some(2)));
-    assert_eq!(async_iter.as_mut().poll_next(&mut cx), Poll::Ready(None));
+    assert_eq!(pin!(async_iter.next()).poll(&mut cx), Poll::Ready(Some(0)));
+    assert_eq!(pin!(async_iter.next()).poll(&mut cx), Poll::Ready(Some(1)));
+    assert_eq!(pin!(async_iter.next()).poll(&mut cx), Poll::Ready(Some(2)));
+    assert_eq!(pin!(async_iter.next()).poll(&mut cx), Poll::Ready(None));
 }
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index 13fb97fdc7f3e..cdaddd8bfe0f0 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -4,8 +4,10 @@
 #![feature(array_windows)]
 #![feature(ascii_char)]
 #![feature(ascii_char_variants)]
-#![feature(async_iter_from_iter)]
 #![feature(async_iterator)]
+#![feature(async_iter_from_iter)]
+#![feature(async_stream)]
+#![feature(async_stream_from_iter)]
 #![feature(bigint_helper_methods)]
 #![feature(cell_update)]
 #![feature(const_align_offset)]
@@ -161,6 +163,7 @@ mod simd;
 mod slice;
 mod str;
 mod str_lossy;
+mod stream;
 mod task;
 mod time;
 mod tuple;
diff --git a/library/core/tests/stream/mod.rs b/library/core/tests/stream/mod.rs
new file mode 100644
index 0000000000000..e59fab4066b57
--- /dev/null
+++ b/library/core/tests/stream/mod.rs
@@ -0,0 +1,17 @@
+use core::pin::pin;
+use core::stream::{self, IntoStream, Stream};
+use core::task::Poll;
+
+#[test]
+fn into_stream() {
+    let stream = stream::from_iter(0..3);
+    let mut stream = pin!(stream.into_stream());
+
+    let waker = core::task::Waker::noop();
+    let mut cx = &mut core::task::Context::from_waker(&waker);
+
+    assert_eq!(stream.as_mut().poll_next(&mut cx), Poll::Ready(Some(0)));
+    assert_eq!(stream.as_mut().poll_next(&mut cx), Poll::Ready(Some(1)));
+    assert_eq!(stream.as_mut().poll_next(&mut cx), Poll::Ready(Some(2)));
+    assert_eq!(stream.as_mut().poll_next(&mut cx), Poll::Ready(None));
+}
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 568645ddf7311..694484defb755 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -367,6 +367,7 @@
 // tidy-alphabetical-start
 #![feature(assert_matches)]
 #![feature(async_iterator)]
+#![feature(async_stream)]
 #![feature(c_variadic)]
 #![feature(cfg_accessible)]
 #![feature(cfg_eval)]
@@ -531,6 +532,8 @@ pub use core::pin;
 pub use core::ptr;
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::result;
+#[unstable(feature = "async_stream", issue = "79024")]
+pub use core::stream;
 #[stable(feature = "i128", since = "1.26.0")]
 #[allow(deprecated, deprecated_in_future)]
 pub use core::u128;
diff --git a/tests/ui/associated-inherent-types/issue-109071.no_gate.stderr b/tests/ui/associated-inherent-types/issue-109071.no_gate.stderr
index b8366b15a8ae4..1e8ebe0fe6ff4 100644
--- a/tests/ui/associated-inherent-types/issue-109071.no_gate.stderr
+++ b/tests/ui/associated-inherent-types/issue-109071.no_gate.stderr
@@ -42,6 +42,8 @@ LL |     fn T() -> Option<<Windows<T> as IntoAsyncIterator>::Item> {}
    |                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 LL |     fn T() -> Option<<Windows<T> as IntoIterator>::Item> {}
    |                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL |     fn T() -> Option<<Windows<T> as IntoStream>::Item> {}
+   |                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/async-await/feature-async-for-loop.rs b/tests/ui/async-await/feature-async-for-loop.rs
index 42247dd14b0d1..ff13908523b5c 100644
--- a/tests/ui/async-await/feature-async-for-loop.rs
+++ b/tests/ui/async-await/feature-async-for-loop.rs
@@ -1,11 +1,11 @@
 // edition:2021
 // gate-test-async_for_loop
 
-#![feature(async_iter_from_iter, async_iterator)]
+#![feature(async_stream_from_iter, async_stream)]
 
 fn f() {
     let _ = async {
-        for await _i in core::async_iter::from_iter(0..3) {
+        for await _i in core::stream::from_iter(0..3) {
             //~^ ERROR `for await` loops are experimental
         }
     };
@@ -14,7 +14,7 @@ fn f() {
 #[cfg(FALSE)]
 fn g() {
     let _ = async {
-        for await _i in core::async_iter::from_iter(0..3) {
+        for await _i in core::stream::from_iter(0..3) {
             //~^ ERROR `for await` loops are experimental
         }
     };
diff --git a/tests/ui/async-await/feature-async-for-loop.stderr b/tests/ui/async-await/feature-async-for-loop.stderr
index 62ddc4222b85e..d9c0721a71de3 100644
--- a/tests/ui/async-await/feature-async-for-loop.stderr
+++ b/tests/ui/async-await/feature-async-for-loop.stderr
@@ -1,7 +1,7 @@
 error[E0658]: `for await` loops are experimental
   --> $DIR/feature-async-for-loop.rs:8:13
    |
-LL |         for await _i in core::async_iter::from_iter(0..3) {
+LL |         for await _i in core::stream::from_iter(0..3) {
    |             ^^^^^
    |
    = note: see issue #118898 <https://github.com/rust-lang/rust/issues/118898> for more information
@@ -11,7 +11,7 @@ LL |         for await _i in core::async_iter::from_iter(0..3) {
 error[E0658]: `for await` loops are experimental
   --> $DIR/feature-async-for-loop.rs:17:13
    |
-LL |         for await _i in core::async_iter::from_iter(0..3) {
+LL |         for await _i in core::stream::from_iter(0..3) {
    |             ^^^^^
    |
    = note: see issue #118898 <https://github.com/rust-lang/rust/issues/118898> for more information
diff --git a/tests/ui/async-await/for-await-consumes-iter.rs b/tests/ui/async-await/for-await-consumes-iter.rs
index 65bb9e88448ff..b2035aa5a94af 100644
--- a/tests/ui/async-await/for-await-consumes-iter.rs
+++ b/tests/ui/async-await/for-await-consumes-iter.rs
@@ -1,12 +1,12 @@
 // edition: 2021
-#![feature(async_iterator, async_iter_from_iter, const_waker, async_for_loop, noop_waker)]
+#![feature(async_stream, async_stream_from_iter, const_waker, async_for_loop, noop_waker)]
 
 use std::future::Future;
 
 // a test to make sure `for await` consumes the iterator
 
 async fn real_main() {
-    let iter = core::async_iter::from_iter(0..3);
+    let iter = core::stream::from_iter(0..3);
     let mut count = 0;
     for await i in iter {
     }
diff --git a/tests/ui/async-await/for-await-consumes-iter.stderr b/tests/ui/async-await/for-await-consumes-iter.stderr
index a3e5bbcabf5d0..d93b5245eafa9 100644
--- a/tests/ui/async-await/for-await-consumes-iter.stderr
+++ b/tests/ui/async-await/for-await-consumes-iter.stderr
@@ -1,8 +1,8 @@
 error[E0382]: use of moved value: `iter`
   --> $DIR/for-await-consumes-iter.rs:14:20
    |
-LL |     let iter = core::async_iter::from_iter(0..3);
-   |         ---- move occurs because `iter` has type `FromIter<std::ops::Range<i32>>`, which does not implement the `Copy` trait
+LL |     let iter = core::stream::from_iter(0..3);
+   |         ---- move occurs because `iter` has type `std::stream::FromIter<std::ops::Range<i32>>`, which does not implement the `Copy` trait
 LL |     let mut count = 0;
 LL |     for await i in iter {
    |                    ---- `iter` moved due to this method call
@@ -10,8 +10,8 @@ LL |     for await i in iter {
 LL |     for await i in iter {
    |                    ^^^^ value used here after move
    |
-note: `into_async_iter` takes ownership of the receiver `self`, which moves `iter`
-  --> $SRC_DIR/core/src/async_iter/async_iter.rs:LL:COL
+note: `into_stream` takes ownership of the receiver `self`, which moves `iter`
+  --> $SRC_DIR/core/src/stream/stream.rs:LL:COL
 help: you can `clone` the value and consume it, but this might not be your desired behavior
    |
 LL |     for await i in iter.clone() {
diff --git a/tests/ui/async-await/for-await-passthrough.rs b/tests/ui/async-await/for-await-passthrough.rs
index b1a382958a157..27066d23694ea 100644
--- a/tests/ui/async-await/for-await-passthrough.rs
+++ b/tests/ui/async-await/for-await-passthrough.rs
@@ -1,13 +1,13 @@
 // run-pass
 // edition: 2024
 // compile-flags: -Zunstable-options
-#![feature(async_iterator, async_iter_from_iter, const_waker, async_for_loop, noop_waker,
+#![feature(async_stream, async_stream_from_iter, const_waker, async_for_loop, noop_waker,
            gen_blocks)]
 
 use std::future::Future;
 
-async gen fn async_iter() -> i32 {
-    let iter = core::async_iter::from_iter(0..3);
+async gen fn stream() -> i32 {
+    let iter = core::stream::from_iter(0..3);
     for await i in iter {
         yield i + 1;
     }
@@ -16,7 +16,7 @@ async gen fn async_iter() -> i32 {
 // make sure a simple for await loop works
 async fn real_main() {
     let mut count = 1;
-    for await i in async_iter() {
+    for await i in stream() {
         assert_eq!(i, count);
         count += 1;
     }
diff --git a/tests/ui/async-await/for-await.rs b/tests/ui/async-await/for-await.rs
index 00dbdfb2389d5..dec6b9a2f4e33 100644
--- a/tests/ui/async-await/for-await.rs
+++ b/tests/ui/async-await/for-await.rs
@@ -1,12 +1,12 @@
 // run-pass
 // edition: 2021
-#![feature(async_iterator, async_iter_from_iter, const_waker, async_for_loop, noop_waker)]
+#![feature(async_stream, async_stream_from_iter, const_waker, async_for_loop, noop_waker)]
 
 use std::future::Future;
 
 // make sure a simple for await loop works
 async fn real_main() {
-    let iter = core::async_iter::from_iter(0..3);
+    let iter = core::stream::from_iter(0..3);
     let mut count = 0;
     for await i in iter {
         assert_eq!(i, count);
diff --git a/tests/ui/coroutine/async-gen-deduce-yield.rs b/tests/ui/coroutine/async-gen-deduce-yield.rs
index 9ccc8ee41f667..bbe0af5255028 100644
--- a/tests/ui/coroutine/async-gen-deduce-yield.rs
+++ b/tests/ui/coroutine/async-gen-deduce-yield.rs
@@ -1,11 +1,11 @@
 // compile-flags: --edition 2024 -Zunstable-options
 // check-pass
 
-#![feature(async_iterator, gen_blocks)]
+#![feature(async_stream, gen_blocks)]
 
-use std::async_iter::AsyncIterator;
+use std::stream::Stream;
 
-fn deduce() -> impl AsyncIterator<Item = ()> {
+fn deduce() -> impl Stream<Item = ()> {
     async gen {
         yield Default::default();
     }
diff --git a/tests/ui/coroutine/async-gen-yield-ty-is-unit.rs b/tests/ui/coroutine/async-gen-yield-ty-is-unit.rs
index 80c0b69a6f7ed..c28f666c9c515 100644
--- a/tests/ui/coroutine/async-gen-yield-ty-is-unit.rs
+++ b/tests/ui/coroutine/async-gen-yield-ty-is-unit.rs
@@ -1,9 +1,9 @@
 // compile-flags: --edition 2024 -Zunstable-options
 // check-pass
 
-#![feature(async_iterator, gen_blocks, noop_waker)]
+#![feature(async_stream, gen_blocks, noop_waker)]
 
-use std::{async_iter::AsyncIterator, pin::pin, task::{Context, Waker}};
+use std::{stream::Stream, pin::pin, task::{Context, Waker}};
 
 async gen fn gen_fn() -> &'static str {
     yield "hello"
@@ -12,5 +12,5 @@ async gen fn gen_fn() -> &'static str {
 pub fn main() {
     let async_iterator = pin!(gen_fn());
     let ctx = &mut Context::from_waker(Waker::noop());
-    async_iterator.poll_next(ctx);
+    stream.poll_next(ctx);
 }
diff --git a/tests/ui/coroutine/async_gen_fn_iter.rs b/tests/ui/coroutine/async_gen_fn_iter.rs
index 604156b4d373f..64cce95d3eba9 100644
--- a/tests/ui/coroutine/async_gen_fn_iter.rs
+++ b/tests/ui/coroutine/async_gen_fn_iter.rs
@@ -2,7 +2,7 @@
 // compile-flags: -Zunstable-options
 // run-pass
 
-#![feature(gen_blocks, async_iterator)]
+#![feature(gen_blocks, async_stream)]
 #![feature(noop_waker)]
 
 // make sure that a ridiculously simple async gen fn works as an iterator.
@@ -45,14 +45,14 @@ async fn async_main() {
 
 use std::pin::{Pin, pin};
 use std::task::*;
-use std::async_iter::AsyncIterator;
+use std::stream::Stream;
 use std::future::Future;
 
-trait AsyncIterExt {
+trait StreamExt {
     fn next(&mut self) -> Next<'_, Self>;
 }
 
-impl<T> AsyncIterExt for T {
+impl<T> StreamExt for T {
     fn next(&mut self) -> Next<'_, Self> {
         Next { s: self }
     }
@@ -62,7 +62,7 @@ struct Next<'s, S: ?Sized> {
     s: &'s mut S,
 }
 
-impl<'s, S: AsyncIterator> Future for Next<'s, S> where S: Unpin {
+impl<'s, S: Stream> Future for Next<'s, S> where S: Unpin {
     type Output = Option<S::Item>;
 
     fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<S::Item>> {
diff --git a/tests/ui/suggestions/suggest-trait-in-ufcs-in-hrtb.stderr b/tests/ui/suggestions/suggest-trait-in-ufcs-in-hrtb.stderr
index cabaa76a8867d..60d430efcb37e 100644
--- a/tests/ui/suggestions/suggest-trait-in-ufcs-in-hrtb.stderr
+++ b/tests/ui/suggestions/suggest-trait-in-ufcs-in-hrtb.stderr
@@ -10,6 +10,8 @@ LL | impl<S> Foo for Bar<S> where for<'a> <&'a S as IntoAsyncIterator>::Item: Fo
    |                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 LL | impl<S> Foo for Bar<S> where for<'a> <&'a S as IntoIterator>::Item: Foo {}
    |                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL | impl<S> Foo for Bar<S> where for<'a> <&'a S as IntoStream>::Item: Foo {}
+   |                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/typeck/issue-110052.stderr b/tests/ui/typeck/issue-110052.stderr
index 5eb10d9a30e86..ae372d96619f3 100644
--- a/tests/ui/typeck/issue-110052.stderr
+++ b/tests/ui/typeck/issue-110052.stderr
@@ -10,6 +10,8 @@ LL |     for<'iter> dyn Validator<<&'iter I as IntoAsyncIterator>::Item>:,
    |                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 LL |     for<'iter> dyn Validator<<&'iter I as IntoIterator>::Item>:,
    |                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL |     for<'iter> dyn Validator<<&'iter I as IntoStream>::Item>:,
+   |                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: aborting due to 1 previous error