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