about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--library/core/src/ops/control_flow.rs147
-rw-r--r--library/coretests/tests/lib.rs1
-rw-r--r--library/coretests/tests/ops/control_flow.rs12
3 files changed, 160 insertions, 0 deletions
diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs
index 26661b20c12..7489a8bb6e7 100644
--- a/library/core/src/ops/control_flow.rs
+++ b/library/core/src/ops/control_flow.rs
@@ -187,6 +187,80 @@ impl<B, C> ControlFlow<B, C> {
         }
     }
 
+    /// Converts the `ControlFlow` into an `Result` which is `Ok` if the
+    /// `ControlFlow` was `Break` and `Err` if otherwise.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(control_flow_ok)]
+    ///
+    /// use std::ops::ControlFlow;
+    ///
+    /// struct TreeNode<T> {
+    ///     value: T,
+    ///     left: Option<Box<TreeNode<T>>>,
+    ///     right: Option<Box<TreeNode<T>>>,
+    /// }
+    ///
+    /// impl<T> TreeNode<T> {
+    ///     fn find<'a>(&'a self, mut predicate: impl FnMut(&T) -> bool) -> Result<&'a T, ()> {
+    ///         let mut f = |t: &'a T| -> ControlFlow<&'a T> {
+    ///             if predicate(t) {
+    ///                 ControlFlow::Break(t)
+    ///             } else {
+    ///                 ControlFlow::Continue(())
+    ///             }
+    ///         };
+    ///
+    ///         self.traverse_inorder(&mut f).break_ok()
+    ///     }
+    ///
+    ///     fn traverse_inorder<'a, B>(
+    ///         &'a self,
+    ///         f: &mut impl FnMut(&'a T) -> ControlFlow<B>,
+    ///     ) -> ControlFlow<B> {
+    ///         if let Some(left) = &self.left {
+    ///             left.traverse_inorder(f)?;
+    ///         }
+    ///         f(&self.value)?;
+    ///         if let Some(right) = &self.right {
+    ///             right.traverse_inorder(f)?;
+    ///         }
+    ///         ControlFlow::Continue(())
+    ///     }
+    ///
+    ///     fn leaf(value: T) -> Option<Box<TreeNode<T>>> {
+    ///         Some(Box::new(Self {
+    ///             value,
+    ///             left: None,
+    ///             right: None,
+    ///         }))
+    ///     }
+    /// }
+    ///
+    /// let node = TreeNode {
+    ///     value: 0,
+    ///     left: TreeNode::leaf(1),
+    ///     right: Some(Box::new(TreeNode {
+    ///         value: -1,
+    ///         left: TreeNode::leaf(5),
+    ///         right: TreeNode::leaf(2),
+    ///     })),
+    /// };
+    ///
+    /// let res = node.find(|val: &i32| *val > 3);
+    /// assert_eq!(res, Ok(&5));
+    /// ```
+    #[inline]
+    #[unstable(feature = "control_flow_ok", issue = "140266")]
+    pub fn break_ok(self) -> Result<B, C> {
+        match self {
+            ControlFlow::Continue(c) => Err(c),
+            ControlFlow::Break(b) => Ok(b),
+        }
+    }
+
     /// Maps `ControlFlow<B, C>` to `ControlFlow<T, C>` by applying a function
     /// to the break value in case it exists.
     #[inline]
@@ -218,6 +292,79 @@ impl<B, C> ControlFlow<B, C> {
         }
     }
 
+    /// Converts the `ControlFlow` into an `Result` which is `Ok` if the
+    /// `ControlFlow` was `Continue` and `Err` if otherwise.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(control_flow_ok)]
+    ///
+    /// use std::ops::ControlFlow;
+    ///
+    /// struct TreeNode<T> {
+    ///     value: T,
+    ///     left: Option<Box<TreeNode<T>>>,
+    ///     right: Option<Box<TreeNode<T>>>,
+    /// }
+    ///
+    /// impl<T> TreeNode<T> {
+    ///     fn validate<B>(&self, f: &mut impl FnMut(&T) -> ControlFlow<B>) -> Result<(), B> {
+    ///         self.traverse_inorder(f).continue_ok()
+    ///     }
+    ///
+    ///     fn traverse_inorder<B>(&self, f: &mut impl FnMut(&T) -> ControlFlow<B>) -> ControlFlow<B> {
+    ///         if let Some(left) = &self.left {
+    ///             left.traverse_inorder(f)?;
+    ///         }
+    ///         f(&self.value)?;
+    ///         if let Some(right) = &self.right {
+    ///             right.traverse_inorder(f)?;
+    ///         }
+    ///         ControlFlow::Continue(())
+    ///     }
+    ///
+    ///     fn leaf(value: T) -> Option<Box<TreeNode<T>>> {
+    ///         Some(Box::new(Self {
+    ///             value,
+    ///             left: None,
+    ///             right: None,
+    ///         }))
+    ///     }
+    /// }
+    ///
+    /// let node = TreeNode {
+    ///     value: 0,
+    ///     left: TreeNode::leaf(1),
+    ///     right: Some(Box::new(TreeNode {
+    ///         value: -1,
+    ///         left: TreeNode::leaf(5),
+    ///         right: TreeNode::leaf(2),
+    ///     })),
+    /// };
+    ///
+    /// let res = node.validate(&mut |val| {
+    ///     if *val < 0 {
+    ///         return ControlFlow::Break("negative value detected");
+    ///     }
+    ///
+    ///     if *val > 4 {
+    ///         return ControlFlow::Break("too big value detected");
+    ///     }
+    ///
+    ///     ControlFlow::Continue(())
+    /// });
+    /// assert_eq!(res, Err("too big value detected"));
+    /// ```
+    #[inline]
+    #[unstable(feature = "control_flow_ok", issue = "140266")]
+    pub fn continue_ok(self) -> Result<C, B> {
+        match self {
+            ControlFlow::Continue(c) => Ok(c),
+            ControlFlow::Break(b) => Err(b),
+        }
+    }
+
     /// Maps `ControlFlow<B, C>` to `ControlFlow<B, T>` by applying a function
     /// to the continue value in case it exists.
     #[inline]
diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs
index 0a9c0c61c95..a0c594d2d59 100644
--- a/library/coretests/tests/lib.rs
+++ b/library/coretests/tests/lib.rs
@@ -22,6 +22,7 @@
 #![feature(const_ref_cell)]
 #![feature(const_result_trait_fn)]
 #![feature(const_trait_impl)]
+#![feature(control_flow_ok)]
 #![feature(core_float_math)]
 #![feature(core_intrinsics)]
 #![feature(core_intrinsics_fallbacks)]
diff --git a/library/coretests/tests/ops/control_flow.rs b/library/coretests/tests/ops/control_flow.rs
index eacfd63a6c4..1df6599ac4a 100644
--- a/library/coretests/tests/ops/control_flow.rs
+++ b/library/coretests/tests/ops/control_flow.rs
@@ -16,3 +16,15 @@ fn control_flow_discriminants_match_result() {
         discriminant_value(&Result::<i32, i32>::Ok(3)),
     );
 }
+
+#[test]
+fn control_flow_break_ok() {
+    assert_eq!(ControlFlow::<char, i32>::Break('b').break_ok(), Ok('b'));
+    assert_eq!(ControlFlow::<char, i32>::Continue(3).break_ok(), Err(3));
+}
+
+#[test]
+fn control_flow_continue_ok() {
+    assert_eq!(ControlFlow::<char, i32>::Break('b').continue_ok(), Err('b'));
+    assert_eq!(ControlFlow::<char, i32>::Continue(3).continue_ok(), Ok(3));
+}