diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs index ef7e6f9c2f491..9fb4d95df2191 100644 --- a/library/core/src/ops/control_flow.rs +++ b/library/core/src/ops/control_flow.rs @@ -167,6 +167,80 @@ impl ControlFlow { matches!(*self, ControlFlow::Continue(_)) } + /// 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 { + /// value: T, + /// left: Option>>, + /// right: Option>>, + /// } + /// + /// impl TreeNode { + /// 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, + /// ) -> ControlFlow { + /// 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>> { + /// 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 { + match self { + ControlFlow::Continue(c) => Err(c), + ControlFlow::Break(b) => Ok(b), + } + } + /// Converts the `ControlFlow` into an `Option` which is `Some` if the /// `ControlFlow` was `Break` and `None` otherwise. /// @@ -198,6 +272,79 @@ impl ControlFlow { } } + /// 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 { + /// value: T, + /// left: Option>>, + /// right: Option>>, + /// } + /// + /// impl TreeNode { + /// fn validate(&self, f: &mut impl FnMut(&T) -> ControlFlow) -> Result<(), B> { + /// self.traverse_inorder(f).continue_ok() + /// } + /// + /// fn traverse_inorder(&self, f: &mut impl FnMut(&T) -> ControlFlow) -> ControlFlow { + /// 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>> { + /// 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 { + match self { + ControlFlow::Continue(c) => Ok(c), + ControlFlow::Break(b) => Err(b), + } + } + /// Converts the `ControlFlow` into an `Option` which is `Some` if the /// `ControlFlow` was `Continue` and `None` otherwise. /// diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index ef548971aafa1..f2e47d7795e28 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -17,6 +17,7 @@ #![feature(const_eval_select)] #![feature(const_swap_nonoverlapping)] #![feature(const_trait_impl)] +#![feature(control_flow_ok)] #![feature(core_intrinsics)] #![feature(core_intrinsics_fallbacks)] #![feature(core_io_borrowed_buf)] diff --git a/library/coretests/tests/ops/control_flow.rs b/library/coretests/tests/ops/control_flow.rs index eacfd63a6c48f..1df6599ac4a5a 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::::Ok(3)), ); } + +#[test] +fn control_flow_break_ok() { + assert_eq!(ControlFlow::::Break('b').break_ok(), Ok('b')); + assert_eq!(ControlFlow::::Continue(3).break_ok(), Err(3)); +} + +#[test] +fn control_flow_continue_ok() { + assert_eq!(ControlFlow::::Break('b').continue_ok(), Err('b')); + assert_eq!(ControlFlow::::Continue(3).continue_ok(), Ok(3)); +}