use crate::{convert, ops}; /// 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. /// /// Similar to [`Option`] and [`Result`], this enum can be used with the `?` operator /// to return immediately if the [`Break`] variant is present or otherwise continue normally /// with the value inside the [`Continue`] variant. /// /// # Examples /// /// Early-exiting from [`Iterator::try_for_each`]: /// ``` /// 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: /// ``` /// use std::ops::ControlFlow; /// /// pub struct TreeNode { /// value: T, /// left: Option>>, /// right: Option>>, /// } /// /// impl TreeNode { /// pub 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 mut sum = 0; /// /// let res = node.traverse_inorder(&mut |val| { /// if *val < 0 { /// ControlFlow::Break(*val) /// } else { /// sum += *val; /// ControlFlow::Continue(()) /// } /// }); /// assert_eq!(res, ControlFlow::Break(-1)); /// assert_eq!(sum, 6); /// ``` /// /// [`Break`]: ControlFlow::Break /// [`Continue`]: ControlFlow::Continue #[stable(feature = "control_flow_enum_type", since = "1.55.0")] #[rustc_diagnostic_item = "ControlFlow"] #[must_use] // ControlFlow should not implement PartialOrd or Ord, per RFC 3058: // https://rust-lang.github.io/rfcs/3058-try-trait-v2.html#traits-for-controlflow #[derive(Copy, Debug, Hash)] #[derive_const(Clone, PartialEq, Eq)] pub enum ControlFlow { /// Move on to the next phase of the operation as normal. #[stable(feature = "control_flow_enum_type", since = "1.55.0")] #[lang = "Continue"] Continue(C), /// Exit the operation without running subsequent phases. #[stable(feature = "control_flow_enum_type", since = "1.55.0")] #[lang = "Break"] Break(B), // Yes, the order of the variants doesn't match the type parameters. // They're in this order so that `ControlFlow` <-> `Result` // is a no-op conversion in the `Try` implementation. } #[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")] #[rustc_const_unstable(feature = "const_try", issue = "74935")] impl const ops::Try for ControlFlow { type Output = C; type Residual = ControlFlow; #[inline] fn from_output(output: Self::Output) -> Self { ControlFlow::Continue(output) } #[inline] fn branch(self) -> ControlFlow { match self { ControlFlow::Continue(c) => ControlFlow::Continue(c), ControlFlow::Break(b) => ControlFlow::Break(ControlFlow::Break(b)), } } } #[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")] #[rustc_const_unstable(feature = "const_try", issue = "74935")] // Note: manually specifying the residual type instead of using the default to work around // https://github.com/rust-lang/rust/issues/99940 impl const ops::FromResidual> for ControlFlow { #[inline] fn from_residual(residual: ControlFlow) -> Self { match residual { ControlFlow::Break(b) => ControlFlow::Break(b), } } } #[unstable(feature = "try_trait_v2_residual", issue = "91285")] impl ops::Residual for ControlFlow { type TryType = ControlFlow; } impl ControlFlow { /// Returns `true` if this is a `Break` variant. /// /// # Examples /// /// ``` /// use std::ops::ControlFlow; /// /// assert!(ControlFlow::<&str, i32>::Break("Stop right there!").is_break()); /// assert!(!ControlFlow::<&str, i32>::Continue(3).is_break()); /// ``` #[inline] #[stable(feature = "control_flow_enum_is", since = "1.59.0")] pub fn is_break(&self) -> bool { matches!(*self, ControlFlow::Break(_)) } /// Returns `true` if this is a `Continue` variant. /// /// # Examples /// /// ``` /// use std::ops::ControlFlow; /// /// assert!(!ControlFlow::<&str, i32>::Break("Stop right there!").is_continue()); /// assert!(ControlFlow::<&str, i32>::Continue(3).is_continue()); /// ``` #[inline] #[stable(feature = "control_flow_enum_is", since = "1.59.0")] pub fn is_continue(&self) -> bool { matches!(*self, ControlFlow::Continue(_)) } /// Converts the `ControlFlow` into an `Option` which is `Some` if the /// `ControlFlow` was `Break` and `None` otherwise. /// /// # Examples /// /// ``` /// use std::ops::ControlFlow; /// /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").break_value(), Some("Stop right there!")); /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).break_value(), None); /// ``` #[inline] #[stable(feature = "control_flow_enum", since = "1.83.0")] pub fn break_value(self) -> Option { match self { ControlFlow::Continue(..) => None, ControlFlow::Break(x) => Some(x), } } /// 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), } } /// Maps `ControlFlow` to `ControlFlow` by applying a function /// to the break value in case it exists. #[inline] #[stable(feature = "control_flow_enum", since = "1.83.0")] pub fn map_break(self, f: impl FnOnce(B) -> T) -> ControlFlow { match self { ControlFlow::Continue(x) => ControlFlow::Continue(x), ControlFlow::Break(x) => ControlFlow::Break(f(x)), } } /// Converts the `ControlFlow` into an `Option` which is `Some` if the /// `ControlFlow` was `Continue` and `None` otherwise. /// /// # Examples /// /// ``` /// use std::ops::ControlFlow; /// /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").continue_value(), None); /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).continue_value(), Some(3)); /// ``` #[inline] #[stable(feature = "control_flow_enum", since = "1.83.0")] pub fn continue_value(self) -> Option { match self { ControlFlow::Continue(x) => Some(x), ControlFlow::Break(..) => None, } } /// 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), } } /// Maps `ControlFlow` to `ControlFlow` by applying a function /// to the continue value in case it exists. #[inline] #[stable(feature = "control_flow_enum", since = "1.83.0")] pub fn map_continue(self, f: impl FnOnce(C) -> T) -> ControlFlow { match self { ControlFlow::Continue(x) => ControlFlow::Continue(f(x)), ControlFlow::Break(x) => ControlFlow::Break(x), } } } impl ControlFlow { /// Extracts the value `T` that is wrapped by `ControlFlow`. /// /// # Examples /// /// ``` /// #![feature(control_flow_into_value)] /// use std::ops::ControlFlow; /// /// assert_eq!(ControlFlow::::Break(1024).into_value(), 1024); /// assert_eq!(ControlFlow::::Continue(512).into_value(), 512); /// ``` #[unstable(feature = "control_flow_into_value", issue = "137461")] #[rustc_allow_const_fn_unstable(const_precise_live_drops)] pub const fn into_value(self) -> T { match self { ControlFlow::Continue(x) | ControlFlow::Break(x) => x, } } } /// These are used only as part of implementing the iterator adapters. /// They have mediocre names and non-obvious semantics, so aren't /// currently on a path to potential stabilization. impl ControlFlow { /// Creates a `ControlFlow` from any type implementing `Try`. #[inline] pub(crate) fn from_try(r: R) -> Self { match R::branch(r) { ControlFlow::Continue(v) => ControlFlow::Continue(v), ControlFlow::Break(v) => ControlFlow::Break(R::from_residual(v)), } } /// Converts a `ControlFlow` into any type implementing `Try`. #[inline] pub(crate) fn into_try(self) -> R { match self { ControlFlow::Continue(v) => R::from_output(v), ControlFlow::Break(v) => v, } } }