control_flow.rs - source
Description: Source of the Rust file `library/core/src/ops/control_flow.rs`.

#
core/ops/
control_flow.rs

    1use crate::marker::Destruct;
    2use crate::{convert, ops};
    3
    4/// Used to tell an operation whether it should exit early or go on as usual.
    5///
    6/// This is used when exposing things (like graph traversals or visitors) where
    7/// you want the user to be able to choose whether to exit early.
    8/// Having the enum makes it clearer -- no more wondering "wait, what did `false`
    9/// mean again?" -- and allows including a value.
    10///
    11/// Similar to [`Option`] and [`Result`], this enum can be used with the `?` operator
    12/// to return immediately if the [`Break`] variant is present or otherwise continue normally
    13/// with the value inside the [`Continue`] variant.
    14///
    15/// # Examples
    16///
    17/// Early-exiting from [`Iterator::try_for_each`]:
    18/// ```
    19/// use std::ops::ControlFlow;
    20///
    21/// let r = (2..100).try_for_each(|x| {
    22///     if 403 % x == 0 {
    23///         return ControlFlow::Break(x)
    24///     }
    25///
    26///     ControlFlow::Continue(())
    27/// });
    28/// assert_eq!(r, ControlFlow::Break(13));
    29/// ```
    30///
    31/// A basic tree traversal:
    32/// ```
    33/// use std::ops::ControlFlow;
    34///
    35/// pub struct TreeNode<T> {
    36///     value: T,
    37///     left: Option<Box<TreeNode<T>>>,
    38///     right: Option<Box<TreeNode<T>>>,
    39/// }
    40///
    41/// impl<T> TreeNode<T> {
    42///     pub fn traverse_inorder<B>(&self, f: &mut impl FnMut(&T) -> ControlFlow<B>) -> ControlFlow<B> {
    43///         if let Some(left) = &self.left {
    44///             left.traverse_inorder(f)?;
    45///         }
    46///         f(&self.value)?;
    47///         if let Some(right) = &self.right {
    48///             right.traverse_inorder(f)?;
    49///         }
    50///         ControlFlow::Continue(())
    51///     }
    52///     fn leaf(value: T) -> Option<Box<TreeNode<T>>> {
    53///         Some(Box::new(Self { value, left: None, right: None }))
    54///     }
    55/// }
    56///
    57/// let node = TreeNode {
    58///     value: 0,
    59///     left: TreeNode::leaf(1),
    60///     right: Some(Box::new(TreeNode {
    61///         value: -1,
    62///         left: TreeNode::leaf(5),
    63///         right: TreeNode::leaf(2),
    64///     }))
    65/// };
    66/// let mut sum = 0;
    67///
    68/// let res = node.traverse_inorder(&mut |val| {
    69///     if *val < 0 {
    70///         ControlFlow::Break(*val)
    71///     } else {
    72///         sum += *val;
    73///         ControlFlow::Continue(())
    74///     }
    75/// });
    76/// assert_eq!(res, ControlFlow::Break(-1));
    77/// assert_eq!(sum, 6);
    78/// ```
    79///
    80/// [`Break`]: ControlFlow::Break
    81/// [`Continue`]: ControlFlow::Continue
    82#[stable(feature = "control_flow_enum_type", since = "1.55.0")]
    83#[rustc_diagnostic_item = "ControlFlow"]
    84#[must_use]
    85// ControlFlow should not implement PartialOrd or Ord, per RFC 3058:
    86// https://rust-lang.github.io/rfcs/3058-try-trait-v2.html#traits-for-controlflow
    87#[derive(Copy, Debug, Hash)]
    88#[derive_const(Clone, PartialEq, Eq)]
    89pub enum ControlFlow<B, C = ()> {
    90    /// Move on to the next phase of the operation as normal.
    91    #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
    92    #[lang = "Continue"]
    93    Continue(C),
    94    /// Exit the operation without running subsequent phases.
    95    #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
    96    #[lang = "Break"]
    97    Break(B),
    98    // Yes, the order of the variants doesn't match the type parameters.
    99    // They're in this order so that `ControlFlow<A, B>` <-> `Result<B, A>`
    100    // is a no-op conversion in the `Try` implementation.
    101}
    102
    103#[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
    104#[rustc_const_unstable(feature = "const_try", issue = "74935")]
    105impl<B, C> const ops::Try for ControlFlow<B, C> {
    106    type Output = C;
    107    type Residual = ControlFlow<B, convert::Infallible>;
    108
    109    #[inline]
    110    fn from_output(output: Self::Output) -> Self {
    111        ControlFlow::Continue(output)
    112    }
    113
    114    #[inline]
    115    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
    116        match self {
    117            ControlFlow::Continue(c) => ControlFlow::Continue(c),
    118            ControlFlow::Break(b) => ControlFlow::Break(ControlFlow::Break(b)),
    119        }
    120    }
    121}
    122
    123#[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
    124#[rustc_const_unstable(feature = "const_try", issue = "74935")]
    125// Note: manually specifying the residual type instead of using the default to work around
    126// https://github.com/rust-lang/rust/issues/99940
    127impl<B, C> const ops::FromResidual<ControlFlow<B, convert::Infallible>> for ControlFlow<B, C> {
    128    #[inline]
    129    fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {
    130        match residual {
    131            ControlFlow::Break(b) => ControlFlow::Break(b),
    132        }
    133    }
    134}
    135
    136#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
    137impl<B, C> ops::Residual<C> for ControlFlow<B, convert::Infallible> {
    138    type TryType = ControlFlow<B, C>;
    139}
    140
    141impl<B, C> ControlFlow<B, C> {
    142    /// Returns `true` if this is a `Break` variant.
    143    ///
    144    /// # Examples
    145    ///
    146    /// ```
    147    /// use std::ops::ControlFlow;
    148    ///
    149    /// assert!(ControlFlow::<&str, i32>::Break("Stop right there!").is_break());
    150    /// assert!(!ControlFlow::<&str, i32>::Continue(3).is_break());
    151    /// ```
    152    #[inline]
    153    #[stable(feature = "control_flow_enum_is", since = "1.59.0")]
    154    #[rustc_const_unstable(feature = "min_const_control_flow", issue = "148738")]
    155    pub const fn is_break(&self) -> bool {
    156        matches!(*self, ControlFlow::Break(_))
    157    }
    158
    159    /// Returns `true` if this is a `Continue` variant.
    160    ///
    161    /// # Examples
    162    ///
    163    /// ```
    164    /// use std::ops::ControlFlow;
    165    ///
    166    /// assert!(!ControlFlow::<&str, i32>::Break("Stop right there!").is_continue());
    167    /// assert!(ControlFlow::<&str, i32>::Continue(3).is_continue());
    168    /// ```
    169    #[inline]
    170    #[stable(feature = "control_flow_enum_is", since = "1.59.0")]
    171    #[rustc_const_unstable(feature = "min_const_control_flow", issue = "148738")]
    172    pub const fn is_continue(&self) -> bool {
    173        matches!(*self, ControlFlow::Continue(_))
    174    }
    175
    176    /// Converts the `ControlFlow` into an `Option` which is `Some` if the
    177    /// `ControlFlow` was `Break` and `None` otherwise.
    178    ///
    179    /// # Examples
    180    ///
    181    /// ```
    182    /// use std::ops::ControlFlow;
    183    ///
    184    /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").break_value(), Some("Stop right there!"));
    185    /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).break_value(), None);
    186    /// ```
    187    #[inline]
    188    #[stable(feature = "control_flow_enum", since = "1.83.0")]
    189    #[rustc_const_unstable(feature = "const_control_flow", issue = "148739")]
    190    pub const fn break_value(self) -> Option<B>
    191    where
    192        Self: [const] Destruct,
    193    {
    194        match self {
    195            ControlFlow::Continue(..) => None,
    196            ControlFlow::Break(x) => Some(x),
    197        }
    198    }
    199
    200    /// Converts the `ControlFlow` into an `Result` which is `Ok` if the
    201    /// `ControlFlow` was `Break` and `Err` if otherwise.
    202    ///
    203    /// # Examples
    204    ///
    205    /// ```
    206    /// #![feature(control_flow_ok)]
    207    ///
    208    /// use std::ops::ControlFlow;
    209    ///
    210    /// struct TreeNode<T> {
    211    ///     value: T,
    212    ///     left: Option<Box<TreeNode<T>>>,
    213    ///     right: Option<Box<TreeNode<T>>>,
    214    /// }
    215    ///
    216    /// impl<T> TreeNode<T> {
    217    ///     fn find<'a>(&'a self, mut predicate: impl FnMut(&T) -> bool) -> Result<&'a T, ()> {
    218    ///         let mut f = |t: &'a T| -> ControlFlow<&'a T> {
    219    ///             if predicate(t) {
    220    ///                 ControlFlow::Break(t)
    221    ///             } else {
    222    ///                 ControlFlow::Continue(())
    223    ///             }
    224    ///         };
    225    ///
    226    ///         self.traverse_inorder(&mut f).break_ok()
    227    ///     }
    228    ///
    229    ///     fn traverse_inorder<'a, B>(
    230    ///         &'a self,
    231    ///         f: &mut impl FnMut(&'a T) -> ControlFlow<B>,
    232    ///     ) -> ControlFlow<B> {
    233    ///         if let Some(left) = &self.left {
    234    ///             left.traverse_inorder(f)?;
    235    ///         }
    236    ///         f(&self.value)?;
    237    ///         if let Some(right) = &self.right {
    238    ///             right.traverse_inorder(f)?;
    239    ///         }
    240    ///         ControlFlow::Continue(())
    241    ///     }
    242    ///
    243    ///     fn leaf(value: T) -> Option<Box<TreeNode<T>>> {
    244    ///         Some(Box::new(Self {
    245    ///             value,
    246    ///             left: None,
    247    ///             right: None,
    248    ///         }))
    249    ///     }
    250    /// }
    251    ///
    252    /// let node = TreeNode {
    253    ///     value: 0,
    254    ///     left: TreeNode::leaf(1),
    255    ///     right: Some(Box::new(TreeNode {
    256    ///         value: -1,
    257    ///         left: TreeNode::leaf(5),
    258    ///         right: TreeNode::leaf(2),
    259    ///     })),
    260    /// };
    261    ///
    262    /// let res = node.find(|val: &i32| *val > 3);
    263    /// assert_eq!(res, Ok(&5));
    264    /// ```
    265    #[inline]
    266    #[unstable(feature = "control_flow_ok", issue = "140266")]
    267    #[rustc_const_unstable(feature = "min_const_control_flow", issue = "148738")]
    268    pub const fn break_ok(self) -> Result<B, C> {
    269        match self {
    270            ControlFlow::Continue(c) => Err(c),
    271            ControlFlow::Break(b) => Ok(b),
    272        }
    273    }
    274
    275    /// Maps `ControlFlow<B, C>` to `ControlFlow<T, C>` by applying a function
    276    /// to the break value in case it exists.
    277    #[inline]
    278    #[stable(feature = "control_flow_enum", since = "1.83.0")]
    279    #[rustc_const_unstable(feature = "const_control_flow", issue = "148739")]
    280    pub const fn map_break<T, F>(self, f: F) -> ControlFlow<T, C>
    281    where
    282        F: [const] FnOnce(B) -> T + [const] Destruct,
    283    {
    284        match self {
    285            ControlFlow::Continue(x) => ControlFlow::Continue(x),
    286            ControlFlow::Break(x) => ControlFlow::Break(f(x)),
    287        }
    288    }
    289
    290    /// Converts the `ControlFlow` into an `Option` which is `Some` if the
    291    /// `ControlFlow` was `Continue` and `None` otherwise.
    292    ///
    293    /// # Examples
    294    ///
    295    /// ```
    296    /// use std::ops::ControlFlow;
    297    ///
    298    /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").continue_value(), None);
    299    /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).continue_value(), Some(3));
    300    /// ```
    301    #[inline]
    302    #[stable(feature = "control_flow_enum", since = "1.83.0")]
    303    #[rustc_const_unstable(feature = "const_control_flow", issue = "148739")]
    304    pub const fn continue_value(self) -> Option<C>
    305    where
    306        Self: [const] Destruct,
    307    {
    308        match self {
    309            ControlFlow::Continue(x) => Some(x),
    310            ControlFlow::Break(..) => None,
    311        }
    312    }
    313
    314    /// Converts the `ControlFlow` into an `Result` which is `Ok` if the
    315    /// `ControlFlow` was `Continue` and `Err` if otherwise.
    316    ///
    317    /// # Examples
    318    ///
    319    /// ```
    320    /// #![feature(control_flow_ok)]
    321    ///
    322    /// use std::ops::ControlFlow;
    323    ///
    324    /// struct TreeNode<T> {
    325    ///     value: T,
    326    ///     left: Option<Box<TreeNode<T>>>,
    327    ///     right: Option<Box<TreeNode<T>>>,
    328    /// }
    329    ///
    330    /// impl<T> TreeNode<T> {
    331    ///     fn validate<B>(&self, f: &mut impl FnMut(&T) -> ControlFlow<B>) -> Result<(), B> {
    332    ///         self.traverse_inorder(f).continue_ok()
    333    ///     }
    334    ///
    335    ///     fn traverse_inorder<B>(&self, f: &mut impl FnMut(&T) -> ControlFlow<B>) -> ControlFlow<B> {
    336    ///         if let Some(left) = &self.left {
    337    ///             left.traverse_inorder(f)?;
    338    ///         }
    339    ///         f(&self.value)?;
    340    ///         if let Some(right) = &self.right {
    341    ///             right.traverse_inorder(f)?;
    342    ///         }
    343    ///         ControlFlow::Continue(())
    344    ///     }
    345    ///
    346    ///     fn leaf(value: T) -> Option<Box<TreeNode<T>>> {
    347    ///         Some(Box::new(Self {
    348    ///             value,
    349    ///             left: None,
    350    ///             right: None,
    351    ///         }))
    352    ///     }
    353    /// }
    354    ///
    355    /// let node = TreeNode {
    356    ///     value: 0,
    357    ///     left: TreeNode::leaf(1),
    358    ///     right: Some(Box::new(TreeNode {
    359    ///         value: -1,
    360    ///         left: TreeNode::leaf(5),
    361    ///         right: TreeNode::leaf(2),
    362    ///     })),
    363    /// };
    364    ///
    365    /// let res = node.validate(&mut |val| {
    366    ///     if *val < 0 {
    367    ///         return ControlFlow::Break("negative value detected");
    368    ///     }
    369    ///
    370    ///     if *val > 4 {
    371    ///         return ControlFlow::Break("too big value detected");
    372    ///     }
    373    ///
    374    ///     ControlFlow::Continue(())
    375    /// });
    376    /// assert_eq!(res, Err("too big value detected"));
    377    /// ```
    378    #[inline]
    379    #[unstable(feature = "control_flow_ok", issue = "140266")]
    380    #[rustc_const_unstable(feature = "min_const_control_flow", issue = "148738")]
    381    pub const fn continue_ok(self) -> Result<C, B> {
    382        match self {
    383            ControlFlow::Continue(c) => Ok(c),
    384            ControlFlow::Break(b) => Err(b),
    385        }
    386    }
    387
    388    /// Maps `ControlFlow<B, C>` to `ControlFlow<B, T>` by applying a function
    389    /// to the continue value in case it exists.
    390    #[inline]
    391    #[stable(feature = "control_flow_enum", since = "1.83.0")]
    392    #[rustc_const_unstable(feature = "const_control_flow", issue = "148739")]
    393    pub const fn map_continue<T, F>(self, f: F) -> ControlFlow<B, T>
    394    where
    395        F: [const] FnOnce(C) -> T + [const] Destruct,
    396    {
    397        match self {
    398            ControlFlow::Continue(x) => ControlFlow::Continue(f(x)),
    399            ControlFlow::Break(x) => ControlFlow::Break(x),
    400        }
    401    }
    402}
    403
    404impl<T> ControlFlow<T, T> {
    405    /// Extracts the value `T` that is wrapped by `ControlFlow<T, T>`.
    406    ///
    407    /// # Examples
    408    ///
    409    /// ```
    410    /// #![feature(control_flow_into_value)]
    411    /// use std::ops::ControlFlow;
    412    ///
    413    /// assert_eq!(ControlFlow::<i32, i32>::Break(1024).into_value(), 1024);
    414    /// assert_eq!(ControlFlow::<i32, i32>::Continue(512).into_value(), 512);
    415    /// ```
    416    #[unstable(feature = "control_flow_into_value", issue = "137461")]
    417    #[rustc_allow_const_fn_unstable(const_precise_live_drops)]
    418    pub const fn into_value(self) -> T {
    419        match self {
    420            ControlFlow::Continue(x) | ControlFlow::Break(x) => x,
    421        }
    422    }
    423}
    424
    425/// These are used only as part of implementing the iterator adapters.
    426/// They have mediocre names and non-obvious semantics, so aren't
    427/// currently on a path to potential stabilization.
    428impl<R: ops::Try> ControlFlow<R, R::Output> {
    429    /// Creates a `ControlFlow` from any type implementing `Try`.
    430    #[inline]
    431    pub(crate) fn from_try(r: R) -> Self {
    432        match R::branch(r) {
    433            ControlFlow::Continue(v) => ControlFlow::Continue(v),
    434            ControlFlow::Break(v) => ControlFlow::Break(R::from_residual(v)),
    435        }
    436    }
    437
    438    /// Converts a `ControlFlow` into any type implementing `Try`.
    439    #[inline]
    440    pub(crate) fn into_try(self) -> R {
    441        match self {
    442            ControlFlow::Continue(v) => R::from_output(v),
    443            ControlFlow::Break(v) => v,
    444        }
    445    }
    446}