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