core/ops/
control_flow.rs

1use crate::{convert, ops};
2
3/// Used to tell an operation whether it should exit early or go on as usual.
4///
5/// This is used when exposing things (like graph traversals or visitors) where
6/// you want the user to be able to choose whether to exit early.
7/// Having the enum makes it clearer -- no more wondering "wait, what did `false`
8/// mean again?" -- and allows including a value.
9///
10/// Similar to [`Option`] and [`Result`], this enum can be used with the `?` operator
11/// to return immediately if the [`Break`] variant is present or otherwise continue normally
12/// with the value inside the [`Continue`] variant.
13///
14/// # Examples
15///
16/// Early-exiting from [`Iterator::try_for_each`]:
17/// ```
18/// use std::ops::ControlFlow;
19///
20/// let r = (2..100).try_for_each(|x| {
21///     if 403 % x == 0 {
22///         return ControlFlow::Break(x)
23///     }
24///
25///     ControlFlow::Continue(())
26/// });
27/// assert_eq!(r, ControlFlow::Break(13));
28/// ```
29///
30/// A basic tree traversal:
31/// ```
32/// use std::ops::ControlFlow;
33///
34/// pub struct TreeNode<T> {
35///     value: T,
36///     left: Option<Box<TreeNode<T>>>,
37///     right: Option<Box<TreeNode<T>>>,
38/// }
39///
40/// impl<T> TreeNode<T> {
41///     pub fn traverse_inorder<B>(&self, f: &mut impl FnMut(&T) -> ControlFlow<B>) -> ControlFlow<B> {
42///         if let Some(left) = &self.left {
43///             left.traverse_inorder(f)?;
44///         }
45///         f(&self.value)?;
46///         if let Some(right) = &self.right {
47///             right.traverse_inorder(f)?;
48///         }
49///         ControlFlow::Continue(())
50///     }
51///     fn leaf(value: T) -> Option<Box<TreeNode<T>>> {
52///         Some(Box::new(Self { value, left: None, right: None }))
53///     }
54/// }
55///
56/// let node = TreeNode {
57///     value: 0,
58///     left: TreeNode::leaf(1),
59///     right: Some(Box::new(TreeNode {
60///         value: -1,
61///         left: TreeNode::leaf(5),
62///         right: TreeNode::leaf(2),
63///     }))
64/// };
65/// let mut sum = 0;
66///
67/// let res = node.traverse_inorder(&mut |val| {
68///     if *val < 0 {
69///         ControlFlow::Break(*val)
70///     } else {
71///         sum += *val;
72///         ControlFlow::Continue(())
73///     }
74/// });
75/// assert_eq!(res, ControlFlow::Break(-1));
76/// assert_eq!(sum, 6);
77/// ```
78///
79/// [`Break`]: ControlFlow::Break
80/// [`Continue`]: ControlFlow::Continue
81#[stable(feature = "control_flow_enum_type", since = "1.55.0")]
82#[cfg_attr(not(test), rustc_diagnostic_item = "ControlFlow")]
83// ControlFlow should not implement PartialOrd or Ord, per RFC 3058:
84// https://rust-lang.github.io/rfcs/3058-try-trait-v2.html#traits-for-controlflow
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
86pub enum ControlFlow<B, C = ()> {
87    /// Move on to the next phase of the operation as normal.
88    #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
89    #[lang = "Continue"]
90    Continue(C),
91    /// Exit the operation without running subsequent phases.
92    #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
93    #[lang = "Break"]
94    Break(B),
95    // Yes, the order of the variants doesn't match the type parameters.
96    // They're in this order so that `ControlFlow<A, B>` <-> `Result<B, A>`
97    // is a no-op conversion in the `Try` implementation.
98}
99
100#[unstable(feature = "try_trait_v2", issue = "84277")]
101impl<B, C> ops::Try for ControlFlow<B, C> {
102    type Output = C;
103    type Residual = ControlFlow<B, convert::Infallible>;
104
105    #[inline]
106    fn from_output(output: Self::Output) -> Self {
107        ControlFlow::Continue(output)
108    }
109
110    #[inline]
111    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
112        match self {
113            ControlFlow::Continue(c) => ControlFlow::Continue(c),
114            ControlFlow::Break(b) => ControlFlow::Break(ControlFlow::Break(b)),
115        }
116    }
117}
118
119#[unstable(feature = "try_trait_v2", issue = "84277")]
120// Note: manually specifying the residual type instead of using the default to work around
121// https://github.com/rust-lang/rust/issues/99940
122impl<B, C> ops::FromResidual<ControlFlow<B, convert::Infallible>> for ControlFlow<B, C> {
123    #[inline]
124    fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {
125        match residual {
126            ControlFlow::Break(b) => ControlFlow::Break(b),
127        }
128    }
129}
130
131#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
132impl<B, C> ops::Residual<C> for ControlFlow<B, convert::Infallible> {
133    type TryType = ControlFlow<B, C>;
134}
135
136impl<B, C> ControlFlow<B, C> {
137    /// Returns `true` if this is a `Break` variant.
138    ///
139    /// # Examples
140    ///
141    /// ```
142    /// use std::ops::ControlFlow;
143    ///
144    /// assert!(ControlFlow::<&str, i32>::Break("Stop right there!").is_break());
145    /// assert!(!ControlFlow::<&str, i32>::Continue(3).is_break());
146    /// ```
147    #[inline]
148    #[stable(feature = "control_flow_enum_is", since = "1.59.0")]
149    pub fn is_break(&self) -> bool {
150        matches!(*self, ControlFlow::Break(_))
151    }
152
153    /// Returns `true` if this is a `Continue` variant.
154    ///
155    /// # Examples
156    ///
157    /// ```
158    /// use std::ops::ControlFlow;
159    ///
160    /// assert!(!ControlFlow::<&str, i32>::Break("Stop right there!").is_continue());
161    /// assert!(ControlFlow::<&str, i32>::Continue(3).is_continue());
162    /// ```
163    #[inline]
164    #[stable(feature = "control_flow_enum_is", since = "1.59.0")]
165    pub fn is_continue(&self) -> bool {
166        matches!(*self, ControlFlow::Continue(_))
167    }
168
169    /// Converts the `ControlFlow` into an `Option` which is `Some` if the
170    /// `ControlFlow` was `Break` and `None` otherwise.
171    ///
172    /// # Examples
173    ///
174    /// ```
175    /// use std::ops::ControlFlow;
176    ///
177    /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").break_value(), Some("Stop right there!"));
178    /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).break_value(), None);
179    /// ```
180    #[inline]
181    #[stable(feature = "control_flow_enum", since = "1.83.0")]
182    pub fn break_value(self) -> Option<B> {
183        match self {
184            ControlFlow::Continue(..) => None,
185            ControlFlow::Break(x) => Some(x),
186        }
187    }
188
189    /// Maps `ControlFlow<B, C>` to `ControlFlow<T, C>` by applying a function
190    /// to the break value in case it exists.
191    #[inline]
192    #[stable(feature = "control_flow_enum", since = "1.83.0")]
193    pub fn map_break<T>(self, f: impl FnOnce(B) -> T) -> ControlFlow<T, C> {
194        match self {
195            ControlFlow::Continue(x) => ControlFlow::Continue(x),
196            ControlFlow::Break(x) => ControlFlow::Break(f(x)),
197        }
198    }
199
200    /// Converts the `ControlFlow` into an `Option` which is `Some` if the
201    /// `ControlFlow` was `Continue` and `None` otherwise.
202    ///
203    /// # Examples
204    ///
205    /// ```
206    /// use std::ops::ControlFlow;
207    ///
208    /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").continue_value(), None);
209    /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).continue_value(), Some(3));
210    /// ```
211    #[inline]
212    #[stable(feature = "control_flow_enum", since = "1.83.0")]
213    pub fn continue_value(self) -> Option<C> {
214        match self {
215            ControlFlow::Continue(x) => Some(x),
216            ControlFlow::Break(..) => None,
217        }
218    }
219
220    /// Maps `ControlFlow<B, C>` to `ControlFlow<B, T>` by applying a function
221    /// to the continue value in case it exists.
222    #[inline]
223    #[stable(feature = "control_flow_enum", since = "1.83.0")]
224    pub fn map_continue<T>(self, f: impl FnOnce(C) -> T) -> ControlFlow<B, T> {
225        match self {
226            ControlFlow::Continue(x) => ControlFlow::Continue(f(x)),
227            ControlFlow::Break(x) => ControlFlow::Break(x),
228        }
229    }
230}
231
232/// These are used only as part of implementing the iterator adapters.
233/// They have mediocre names and non-obvious semantics, so aren't
234/// currently on a path to potential stabilization.
235impl<R: ops::Try> ControlFlow<R, R::Output> {
236    /// Creates a `ControlFlow` from any type implementing `Try`.
237    #[inline]
238    pub(crate) fn from_try(r: R) -> Self {
239        match R::branch(r) {
240            ControlFlow::Continue(v) => ControlFlow::Continue(v),
241            ControlFlow::Break(v) => ControlFlow::Break(R::from_residual(v)),
242        }
243    }
244
245    /// Converts a `ControlFlow` into any type implementing `Try`.
246    #[inline]
247    pub(crate) fn into_try(self) -> R {
248        match self {
249            ControlFlow::Continue(v) => R::from_output(v),
250            ControlFlow::Break(v) => v,
251        }
252    }
253}