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#[rustc_diagnostic_item = "ControlFlow"]
83#[must_use]
84// ControlFlow should not implement PartialOrd or Ord, per RFC 3058:
85// https://rust-lang.github.io/rfcs/3058-try-trait-v2.html#traits-for-controlflow
86#[derive(Copy, Debug, Hash)]
87#[derive_const(Clone, PartialEq, Eq)]
88pub enum ControlFlow<B, C = ()> {
89    /// Move on to the next phase of the operation as normal.
90    #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
91    #[lang = "Continue"]
92    Continue(C),
93    /// Exit the operation without running subsequent phases.
94    #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
95    #[lang = "Break"]
96    Break(B),
97    // Yes, the order of the variants doesn't match the type parameters.
98    // They're in this order so that `ControlFlow<A, B>` <-> `Result<B, A>`
99    // is a no-op conversion in the `Try` implementation.
100}
101
102#[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
103#[rustc_const_unstable(feature = "const_try", issue = "74935")]
104impl<B, C> const ops::Try for ControlFlow<B, C> {
105    type Output = C;
106    type Residual = ControlFlow<B, convert::Infallible>;
107
108    #[inline]
109    fn from_output(output: Self::Output) -> Self {
110        ControlFlow::Continue(output)
111    }
112
113    #[inline]
114    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
115        match self {
116            ControlFlow::Continue(c) => ControlFlow::Continue(c),
117            ControlFlow::Break(b) => ControlFlow::Break(ControlFlow::Break(b)),
118        }
119    }
120}
121
122#[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
123#[rustc_const_unstable(feature = "const_try", issue = "74935")]
124// Note: manually specifying the residual type instead of using the default to work around
125// https://github.com/rust-lang/rust/issues/99940
126impl<B, C> const ops::FromResidual<ControlFlow<B, convert::Infallible>> for ControlFlow<B, C> {
127    #[inline]
128    fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {
129        match residual {
130            ControlFlow::Break(b) => ControlFlow::Break(b),
131        }
132    }
133}
134
135#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
136impl<B, C> ops::Residual<C> for ControlFlow<B, convert::Infallible> {
137    type TryType = ControlFlow<B, C>;
138}
139
140impl<B, C> ControlFlow<B, C> {
141    /// Returns `true` if this is a `Break` variant.
142    ///
143    /// # Examples
144    ///
145    /// ```
146    /// use std::ops::ControlFlow;
147    ///
148    /// assert!(ControlFlow::<&str, i32>::Break("Stop right there!").is_break());
149    /// assert!(!ControlFlow::<&str, i32>::Continue(3).is_break());
150    /// ```
151    #[inline]
152    #[stable(feature = "control_flow_enum_is", since = "1.59.0")]
153    pub fn is_break(&self) -> bool {
154        matches!(*self, ControlFlow::Break(_))
155    }
156
157    /// Returns `true` if this is a `Continue` variant.
158    ///
159    /// # Examples
160    ///
161    /// ```
162    /// use std::ops::ControlFlow;
163    ///
164    /// assert!(!ControlFlow::<&str, i32>::Break("Stop right there!").is_continue());
165    /// assert!(ControlFlow::<&str, i32>::Continue(3).is_continue());
166    /// ```
167    #[inline]
168    #[stable(feature = "control_flow_enum_is", since = "1.59.0")]
169    pub fn is_continue(&self) -> bool {
170        matches!(*self, ControlFlow::Continue(_))
171    }
172
173    /// Converts the `ControlFlow` into an `Option` which is `Some` if the
174    /// `ControlFlow` was `Break` and `None` otherwise.
175    ///
176    /// # Examples
177    ///
178    /// ```
179    /// use std::ops::ControlFlow;
180    ///
181    /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").break_value(), Some("Stop right there!"));
182    /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).break_value(), None);
183    /// ```
184    #[inline]
185    #[stable(feature = "control_flow_enum", since = "1.83.0")]
186    pub fn break_value(self) -> Option<B> {
187        match self {
188            ControlFlow::Continue(..) => None,
189            ControlFlow::Break(x) => Some(x),
190        }
191    }
192
193    /// Converts the `ControlFlow` into an `Result` which is `Ok` if the
194    /// `ControlFlow` was `Break` and `Err` if otherwise.
195    ///
196    /// # Examples
197    ///
198    /// ```
199    /// #![feature(control_flow_ok)]
200    ///
201    /// use std::ops::ControlFlow;
202    ///
203    /// struct TreeNode<T> {
204    ///     value: T,
205    ///     left: Option<Box<TreeNode<T>>>,
206    ///     right: Option<Box<TreeNode<T>>>,
207    /// }
208    ///
209    /// impl<T> TreeNode<T> {
210    ///     fn find<'a>(&'a self, mut predicate: impl FnMut(&T) -> bool) -> Result<&'a T, ()> {
211    ///         let mut f = |t: &'a T| -> ControlFlow<&'a T> {
212    ///             if predicate(t) {
213    ///                 ControlFlow::Break(t)
214    ///             } else {
215    ///                 ControlFlow::Continue(())
216    ///             }
217    ///         };
218    ///
219    ///         self.traverse_inorder(&mut f).break_ok()
220    ///     }
221    ///
222    ///     fn traverse_inorder<'a, B>(
223    ///         &'a self,
224    ///         f: &mut impl FnMut(&'a T) -> ControlFlow<B>,
225    ///     ) -> ControlFlow<B> {
226    ///         if let Some(left) = &self.left {
227    ///             left.traverse_inorder(f)?;
228    ///         }
229    ///         f(&self.value)?;
230    ///         if let Some(right) = &self.right {
231    ///             right.traverse_inorder(f)?;
232    ///         }
233    ///         ControlFlow::Continue(())
234    ///     }
235    ///
236    ///     fn leaf(value: T) -> Option<Box<TreeNode<T>>> {
237    ///         Some(Box::new(Self {
238    ///             value,
239    ///             left: None,
240    ///             right: None,
241    ///         }))
242    ///     }
243    /// }
244    ///
245    /// let node = TreeNode {
246    ///     value: 0,
247    ///     left: TreeNode::leaf(1),
248    ///     right: Some(Box::new(TreeNode {
249    ///         value: -1,
250    ///         left: TreeNode::leaf(5),
251    ///         right: TreeNode::leaf(2),
252    ///     })),
253    /// };
254    ///
255    /// let res = node.find(|val: &i32| *val > 3);
256    /// assert_eq!(res, Ok(&5));
257    /// ```
258    #[inline]
259    #[unstable(feature = "control_flow_ok", issue = "140266")]
260    pub fn break_ok(self) -> Result<B, C> {
261        match self {
262            ControlFlow::Continue(c) => Err(c),
263            ControlFlow::Break(b) => Ok(b),
264        }
265    }
266
267    /// Maps `ControlFlow<B, C>` to `ControlFlow<T, C>` by applying a function
268    /// to the break value in case it exists.
269    #[inline]
270    #[stable(feature = "control_flow_enum", since = "1.83.0")]
271    pub fn map_break<T>(self, f: impl FnOnce(B) -> T) -> ControlFlow<T, C> {
272        match self {
273            ControlFlow::Continue(x) => ControlFlow::Continue(x),
274            ControlFlow::Break(x) => ControlFlow::Break(f(x)),
275        }
276    }
277
278    /// Converts the `ControlFlow` into an `Option` which is `Some` if the
279    /// `ControlFlow` was `Continue` and `None` otherwise.
280    ///
281    /// # Examples
282    ///
283    /// ```
284    /// use std::ops::ControlFlow;
285    ///
286    /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").continue_value(), None);
287    /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).continue_value(), Some(3));
288    /// ```
289    #[inline]
290    #[stable(feature = "control_flow_enum", since = "1.83.0")]
291    pub fn continue_value(self) -> Option<C> {
292        match self {
293            ControlFlow::Continue(x) => Some(x),
294            ControlFlow::Break(..) => None,
295        }
296    }
297
298    /// Converts the `ControlFlow` into an `Result` which is `Ok` if the
299    /// `ControlFlow` was `Continue` and `Err` if otherwise.
300    ///
301    /// # Examples
302    ///
303    /// ```
304    /// #![feature(control_flow_ok)]
305    ///
306    /// use std::ops::ControlFlow;
307    ///
308    /// struct TreeNode<T> {
309    ///     value: T,
310    ///     left: Option<Box<TreeNode<T>>>,
311    ///     right: Option<Box<TreeNode<T>>>,
312    /// }
313    ///
314    /// impl<T> TreeNode<T> {
315    ///     fn validate<B>(&self, f: &mut impl FnMut(&T) -> ControlFlow<B>) -> Result<(), B> {
316    ///         self.traverse_inorder(f).continue_ok()
317    ///     }
318    ///
319    ///     fn traverse_inorder<B>(&self, f: &mut impl FnMut(&T) -> ControlFlow<B>) -> ControlFlow<B> {
320    ///         if let Some(left) = &self.left {
321    ///             left.traverse_inorder(f)?;
322    ///         }
323    ///         f(&self.value)?;
324    ///         if let Some(right) = &self.right {
325    ///             right.traverse_inorder(f)?;
326    ///         }
327    ///         ControlFlow::Continue(())
328    ///     }
329    ///
330    ///     fn leaf(value: T) -> Option<Box<TreeNode<T>>> {
331    ///         Some(Box::new(Self {
332    ///             value,
333    ///             left: None,
334    ///             right: None,
335    ///         }))
336    ///     }
337    /// }
338    ///
339    /// let node = TreeNode {
340    ///     value: 0,
341    ///     left: TreeNode::leaf(1),
342    ///     right: Some(Box::new(TreeNode {
343    ///         value: -1,
344    ///         left: TreeNode::leaf(5),
345    ///         right: TreeNode::leaf(2),
346    ///     })),
347    /// };
348    ///
349    /// let res = node.validate(&mut |val| {
350    ///     if *val < 0 {
351    ///         return ControlFlow::Break("negative value detected");
352    ///     }
353    ///
354    ///     if *val > 4 {
355    ///         return ControlFlow::Break("too big value detected");
356    ///     }
357    ///
358    ///     ControlFlow::Continue(())
359    /// });
360    /// assert_eq!(res, Err("too big value detected"));
361    /// ```
362    #[inline]
363    #[unstable(feature = "control_flow_ok", issue = "140266")]
364    pub fn continue_ok(self) -> Result<C, B> {
365        match self {
366            ControlFlow::Continue(c) => Ok(c),
367            ControlFlow::Break(b) => Err(b),
368        }
369    }
370
371    /// Maps `ControlFlow<B, C>` to `ControlFlow<B, T>` by applying a function
372    /// to the continue value in case it exists.
373    #[inline]
374    #[stable(feature = "control_flow_enum", since = "1.83.0")]
375    pub fn map_continue<T>(self, f: impl FnOnce(C) -> T) -> ControlFlow<B, T> {
376        match self {
377            ControlFlow::Continue(x) => ControlFlow::Continue(f(x)),
378            ControlFlow::Break(x) => ControlFlow::Break(x),
379        }
380    }
381}
382
383impl<T> ControlFlow<T, T> {
384    /// Extracts the value `T` that is wrapped by `ControlFlow<T, T>`.
385    ///
386    /// # Examples
387    ///
388    /// ```
389    /// #![feature(control_flow_into_value)]
390    /// use std::ops::ControlFlow;
391    ///
392    /// assert_eq!(ControlFlow::<i32, i32>::Break(1024).into_value(), 1024);
393    /// assert_eq!(ControlFlow::<i32, i32>::Continue(512).into_value(), 512);
394    /// ```
395    #[unstable(feature = "control_flow_into_value", issue = "137461")]
396    #[rustc_allow_const_fn_unstable(const_precise_live_drops)]
397    pub const fn into_value(self) -> T {
398        match self {
399            ControlFlow::Continue(x) | ControlFlow::Break(x) => x,
400        }
401    }
402}
403
404/// These are used only as part of implementing the iterator adapters.
405/// They have mediocre names and non-obvious semantics, so aren't
406/// currently on a path to potential stabilization.
407impl<R: ops::Try> ControlFlow<R, R::Output> {
408    /// Creates a `ControlFlow` from any type implementing `Try`.
409    #[inline]
410    pub(crate) fn from_try(r: R) -> Self {
411        match R::branch(r) {
412            ControlFlow::Continue(v) => ControlFlow::Continue(v),
413            ControlFlow::Break(v) => ControlFlow::Break(R::from_residual(v)),
414        }
415    }
416
417    /// Converts a `ControlFlow` into any type implementing `Try`.
418    #[inline]
419    pub(crate) fn into_try(self) -> R {
420        match self {
421            ControlFlow::Continue(v) => R::from_output(v),
422            ControlFlow::Break(v) => v,
423        }
424    }
425}