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(Debug, Clone, Copy, PartialEq, Eq, Hash)]
87pub enum ControlFlow<B, C = ()> {
88    /// Move on to the next phase of the operation as normal.
89    #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
90    #[lang = "Continue"]
91    Continue(C),
92    /// Exit the operation without running subsequent phases.
93    #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
94    #[lang = "Break"]
95    Break(B),
96    // Yes, the order of the variants doesn't match the type parameters.
97    // They're in this order so that `ControlFlow<A, B>` <-> `Result<B, A>`
98    // is a no-op conversion in the `Try` implementation.
99}
100
101#[unstable(feature = "try_trait_v2", issue = "84277")]
102impl<B, C> ops::Try for ControlFlow<B, C> {
103    type Output = C;
104    type Residual = ControlFlow<B, convert::Infallible>;
105
106    #[inline]
107    fn from_output(output: Self::Output) -> Self {
108        ControlFlow::Continue(output)
109    }
110
111    #[inline]
112    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
113        match self {
114            ControlFlow::Continue(c) => ControlFlow::Continue(c),
115            ControlFlow::Break(b) => ControlFlow::Break(ControlFlow::Break(b)),
116        }
117    }
118}
119
120#[unstable(feature = "try_trait_v2", issue = "84277")]
121// Note: manually specifying the residual type instead of using the default to work around
122// https://github.com/rust-lang/rust/issues/99940
123impl<B, C> ops::FromResidual<ControlFlow<B, convert::Infallible>> for ControlFlow<B, C> {
124    #[inline]
125    fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {
126        match residual {
127            ControlFlow::Break(b) => ControlFlow::Break(b),
128        }
129    }
130}
131
132#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
133impl<B, C> ops::Residual<C> for ControlFlow<B, convert::Infallible> {
134    type TryType = ControlFlow<B, C>;
135}
136
137impl<B, C> ControlFlow<B, C> {
138    /// Returns `true` if this is a `Break` variant.
139    ///
140    /// # Examples
141    ///
142    /// ```
143    /// use std::ops::ControlFlow;
144    ///
145    /// assert!(ControlFlow::<&str, i32>::Break("Stop right there!").is_break());
146    /// assert!(!ControlFlow::<&str, i32>::Continue(3).is_break());
147    /// ```
148    #[inline]
149    #[stable(feature = "control_flow_enum_is", since = "1.59.0")]
150    pub fn is_break(&self) -> bool {
151        matches!(*self, ControlFlow::Break(_))
152    }
153
154    /// Returns `true` if this is a `Continue` variant.
155    ///
156    /// # Examples
157    ///
158    /// ```
159    /// use std::ops::ControlFlow;
160    ///
161    /// assert!(!ControlFlow::<&str, i32>::Break("Stop right there!").is_continue());
162    /// assert!(ControlFlow::<&str, i32>::Continue(3).is_continue());
163    /// ```
164    #[inline]
165    #[stable(feature = "control_flow_enum_is", since = "1.59.0")]
166    pub fn is_continue(&self) -> bool {
167        matches!(*self, ControlFlow::Continue(_))
168    }
169
170    /// Converts the `ControlFlow` into an `Option` which is `Some` if the
171    /// `ControlFlow` was `Break` and `None` otherwise.
172    ///
173    /// # Examples
174    ///
175    /// ```
176    /// use std::ops::ControlFlow;
177    ///
178    /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").break_value(), Some("Stop right there!"));
179    /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).break_value(), None);
180    /// ```
181    #[inline]
182    #[stable(feature = "control_flow_enum", since = "1.83.0")]
183    pub fn break_value(self) -> Option<B> {
184        match self {
185            ControlFlow::Continue(..) => None,
186            ControlFlow::Break(x) => Some(x),
187        }
188    }
189
190    /// Maps `ControlFlow<B, C>` to `ControlFlow<T, C>` by applying a function
191    /// to the break value in case it exists.
192    #[inline]
193    #[stable(feature = "control_flow_enum", since = "1.83.0")]
194    pub fn map_break<T>(self, f: impl FnOnce(B) -> T) -> ControlFlow<T, C> {
195        match self {
196            ControlFlow::Continue(x) => ControlFlow::Continue(x),
197            ControlFlow::Break(x) => ControlFlow::Break(f(x)),
198        }
199    }
200
201    /// Converts the `ControlFlow` into an `Option` which is `Some` if the
202    /// `ControlFlow` was `Continue` and `None` otherwise.
203    ///
204    /// # Examples
205    ///
206    /// ```
207    /// use std::ops::ControlFlow;
208    ///
209    /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").continue_value(), None);
210    /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).continue_value(), Some(3));
211    /// ```
212    #[inline]
213    #[stable(feature = "control_flow_enum", since = "1.83.0")]
214    pub fn continue_value(self) -> Option<C> {
215        match self {
216            ControlFlow::Continue(x) => Some(x),
217            ControlFlow::Break(..) => None,
218        }
219    }
220
221    /// Maps `ControlFlow<B, C>` to `ControlFlow<B, T>` by applying a function
222    /// to the continue value in case it exists.
223    #[inline]
224    #[stable(feature = "control_flow_enum", since = "1.83.0")]
225    pub fn map_continue<T>(self, f: impl FnOnce(C) -> T) -> ControlFlow<B, T> {
226        match self {
227            ControlFlow::Continue(x) => ControlFlow::Continue(f(x)),
228            ControlFlow::Break(x) => ControlFlow::Break(x),
229        }
230    }
231}
232
233impl<T> ControlFlow<T, T> {
234    /// Extracts the value `T` that is wrapped by `ControlFlow<T, T>`.
235    ///
236    /// # Examples
237    ///
238    /// ```
239    /// #![feature(control_flow_into_value)]
240    /// use std::ops::ControlFlow;
241    ///
242    /// assert_eq!(ControlFlow::<i32, i32>::Break(1024).into_value(), 1024);
243    /// assert_eq!(ControlFlow::<i32, i32>::Continue(512).into_value(), 512);
244    /// ```
245    #[unstable(feature = "control_flow_into_value", issue = "137461")]
246    #[rustc_allow_const_fn_unstable(const_precise_live_drops)]
247    pub const fn into_value(self) -> T {
248        match self {
249            ControlFlow::Continue(x) | ControlFlow::Break(x) => x,
250        }
251    }
252}
253
254/// These are used only as part of implementing the iterator adapters.
255/// They have mediocre names and non-obvious semantics, so aren't
256/// currently on a path to potential stabilization.
257impl<R: ops::Try> ControlFlow<R, R::Output> {
258    /// Creates a `ControlFlow` from any type implementing `Try`.
259    #[inline]
260    pub(crate) fn from_try(r: R) -> Self {
261        match R::branch(r) {
262            ControlFlow::Continue(v) => ControlFlow::Continue(v),
263            ControlFlow::Break(v) => ControlFlow::Break(R::from_residual(v)),
264        }
265    }
266
267    /// Converts a `ControlFlow` into any type implementing `Try`.
268    #[inline]
269    pub(crate) fn into_try(self) -> R {
270        match self {
271            ControlFlow::Continue(v) => R::from_output(v),
272            ControlFlow::Break(v) => v,
273        }
274    }
275}
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy