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")] // 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(Debug, Clone, Copy, PartialEq, Eq, Hash)] 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")] #[rustc_const_unstable(feature = "const_convert", issue = "88674")] 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")] #[rustc_const_unstable(feature = "const_convert", issue = "88674")] 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")] #[rustc_const_unstable(feature = "const_try", issue = "74935")] impl const ops::Residual for ControlFlow { type TryType = ControlFlow; } impl ControlFlow { /// Returns `true` if this is a `Break` variant. /// /// # Examples /// /// ``` /// use std::ops::ControlFlow; /// /// assert!(ControlFlow::::Break(3).is_break()); /// assert!(!ControlFlow::::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::::Break(3).is_continue()); /// assert!(ControlFlow::::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 /// /// ``` /// #![feature(control_flow_enum)] /// use std::ops::ControlFlow; /// /// assert_eq!(ControlFlow::::Break(3).break_value(), Some(3)); /// assert_eq!(ControlFlow::::Continue(3).break_value(), None); /// ``` #[inline] #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")] pub fn break_value(self) -> Option { match self { ControlFlow::Continue(..) => None, ControlFlow::Break(x) => Some(x), } } /// Maps `ControlFlow` to `ControlFlow` by applying a function /// to the break value in case it exists. #[inline] #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")] pub fn map_break(self, f: F) -> ControlFlow where F: FnOnce(B) -> T, { 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 /// /// ``` /// #![feature(control_flow_enum)] /// use std::ops::ControlFlow; /// /// assert_eq!(ControlFlow::::Break(3).continue_value(), None); /// assert_eq!(ControlFlow::::Continue(3).continue_value(), Some(3)); /// ``` #[inline] #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")] pub fn continue_value(self) -> Option { match self { ControlFlow::Continue(x) => Some(x), ControlFlow::Break(..) => None, } } /// Maps `ControlFlow` to `ControlFlow` by applying a function /// to the continue value in case it exists. #[inline] #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")] pub fn map_continue(self, f: F) -> ControlFlow where F: FnOnce(C) -> T, { match self { ControlFlow::Continue(x) => ControlFlow::Continue(f(x)), ControlFlow::Break(x) => ControlFlow::Break(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 { /// Create 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)), } } /// Convert 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, } } }