Skip to main content

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