diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs
index 4834ca74b82..2f78ba8f28e 100644
--- a/library/core/src/ops/control_flow.rs
+++ b/library/core/src/ops/control_flow.rs
@@ -1,13 +1,63 @@
 use crate::ops::Try;
 
-/// Used to make try_fold closures more like normal loops
+/// Used to tell an operation whether it should exit early or go on as usual.
+///
+/// This is used when exposing things (like graph traversals or visitors) where
+/// you want the user to be able to choose whether to exit early.
+/// Having the enum makes it clearer -- no more wondering "wait, what did `false`
+/// mean again?" -- and allows including a value.
+///
+/// # Examples
+///
+/// Early-exiting from [`Iterator::try_for_each`]:
+/// ```
+/// #![feature(control_flow_enum)]
+/// use std::ops::ControlFlow;
+///
+/// let r = (2..100).try_for_each(|x| {
+///     if 403 % x == 0 {
+///         return ControlFlow::Break(x)
+///     }
+///
+///     ControlFlow::Continue(())
+/// });
+/// assert_eq!(r, ControlFlow::Break(13));
+/// ```
+///
+/// A basic tree traversal:
+/// ```no_run
+/// #![feature(control_flow_enum)]
+/// use std::ops::ControlFlow;
+///
+/// pub struct TreeNode<T> {
+///     value: T,
+///     left: Option<Box<TreeNode<T>>>,
+///     right: Option<Box<TreeNode<T>>>,
+/// }
+///
+/// impl<T> TreeNode<T> {
+///     pub fn traverse_inorder<B>(&self, mut f: impl FnMut(&T) -> ControlFlow<B>) -> ControlFlow<B> {
+///         if let Some(left) = &self.left {
+///             left.traverse_inorder(&mut f)?;
+///         }
+///         f(&self.value)?;
+///         if let Some(right) = &self.right {
+///             right.traverse_inorder(&mut f)?;
+///         }
+///         ControlFlow::Continue(())
+///     }
+/// }
+/// ```
 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
 #[derive(Debug, Clone, Copy, PartialEq)]
 pub enum ControlFlow<B, C = ()> {
-    /// Continue in the loop, using the given value for the next iteration
+    /// Move on to the next phase of the operation as normal.
     Continue(C),
-    /// Exit the loop, yielding the given value
+    /// Exit the operation without running subsequent phases.
     Break(B),
+    // Yes, the order of the variants doesn't match the type parameters.
+    // They're in this order so that `ControlFlow<A, B>` <-> `Result<B, A>`
+    // is a no-op conversion in the `Try` implementation.
 }
 
 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
@@ -33,6 +83,16 @@ impl<B, C> Try for ControlFlow<B, C> {
 
 impl<B, C> ControlFlow<B, C> {
     /// Returns `true` if this is a `Break` variant.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(control_flow_enum)]
+    /// use std::ops::ControlFlow;
+    ///
+    /// assert!(ControlFlow::<i32, String>::Break(3).is_break());
+    /// assert!(!ControlFlow::<String, i32>::Continue(3).is_break());
+    /// ```
     #[inline]
     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
     pub fn is_break(&self) -> bool {
@@ -40,6 +100,16 @@ impl<B, C> ControlFlow<B, C> {
     }
 
     /// Returns `true` if this is a `Continue` variant.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(control_flow_enum)]
+    /// use std::ops::ControlFlow;
+    ///
+    /// assert!(!ControlFlow::<i32, String>::Break(3).is_continue());
+    /// assert!(ControlFlow::<String, i32>::Continue(3).is_continue());
+    /// ```
     #[inline]
     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
     pub fn is_continue(&self) -> bool {
@@ -48,6 +118,16 @@ impl<B, C> ControlFlow<B, C> {
 
     /// Converts the `ControlFlow` into an `Option` which is `Some` if the
     /// `ControlFlow` was `Break` and `None` otherwise.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(control_flow_enum)]
+    /// use std::ops::ControlFlow;
+    ///
+    /// assert_eq!(ControlFlow::<i32, String>::Break(3).break_value(), Some(3));
+    /// assert_eq!(ControlFlow::<String, i32>::Continue(3).break_value(), None);
+    /// ```
     #[inline]
     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
     pub fn break_value(self) -> Option<B> {
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index 4dc86e0f5f4..339691b1176 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -15,6 +15,7 @@
 #![feature(const_maybe_uninit_assume_init)]
 #![feature(const_ptr_read)]
 #![feature(const_ptr_offset)]
+#![feature(control_flow_enum)]
 #![feature(core_intrinsics)]
 #![feature(core_private_bignum)]
 #![feature(core_private_diy_float)]
diff --git a/library/core/tests/ops.rs b/library/core/tests/ops.rs
index 53e5539fad9..aa79dbac8f3 100644
--- a/library/core/tests/ops.rs
+++ b/library/core/tests/ops.rs
@@ -1,3 +1,5 @@
+mod control_flow;
+
 use core::ops::{Bound, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
 use core::ops::{Deref, DerefMut};
 
diff --git a/library/core/tests/ops/control_flow.rs b/library/core/tests/ops/control_flow.rs
new file mode 100644
index 00000000000..eacfd63a6c4
--- /dev/null
+++ b/library/core/tests/ops/control_flow.rs
@@ -0,0 +1,18 @@
+use core::intrinsics::discriminant_value;
+use core::ops::ControlFlow;
+
+#[test]
+fn control_flow_discriminants_match_result() {
+    // This isn't stable surface area, but helps keep `?` cheap between them,
+    // even if LLVM can't always take advantage of it right now.
+    // (Sadly Result and Option are inconsistent, so ControlFlow can't match both.)
+
+    assert_eq!(
+        discriminant_value(&ControlFlow::<i32, i32>::Break(3)),
+        discriminant_value(&Result::<i32, i32>::Err(3)),
+    );
+    assert_eq!(
+        discriminant_value(&ControlFlow::<i32, i32>::Continue(3)),
+        discriminant_value(&Result::<i32, i32>::Ok(3)),
+    );
+}