]> git.lizzy.rs Git - rust.git/blob - library/core/src/ops/control_flow.rs
Auto merge of #88219 - jyn514:parallel-io, r=GuillaumeGomez
[rust.git] / library / core / src / ops / control_flow.rs
1 use 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 /// # Examples
11 ///
12 /// Early-exiting from [`Iterator::try_for_each`]:
13 /// ```
14 /// use std::ops::ControlFlow;
15 ///
16 /// let r = (2..100).try_for_each(|x| {
17 ///     if 403 % x == 0 {
18 ///         return ControlFlow::Break(x)
19 ///     }
20 ///
21 ///     ControlFlow::Continue(())
22 /// });
23 /// assert_eq!(r, ControlFlow::Break(13));
24 /// ```
25 ///
26 /// A basic tree traversal:
27 /// ```no_run
28 /// use std::ops::ControlFlow;
29 ///
30 /// pub struct TreeNode<T> {
31 ///     value: T,
32 ///     left: Option<Box<TreeNode<T>>>,
33 ///     right: Option<Box<TreeNode<T>>>,
34 /// }
35 ///
36 /// impl<T> TreeNode<T> {
37 ///     pub fn traverse_inorder<B>(&self, mut f: impl FnMut(&T) -> ControlFlow<B>) -> ControlFlow<B> {
38 ///         if let Some(left) = &self.left {
39 ///             left.traverse_inorder(&mut f)?;
40 ///         }
41 ///         f(&self.value)?;
42 ///         if let Some(right) = &self.right {
43 ///             right.traverse_inorder(&mut f)?;
44 ///         }
45 ///         ControlFlow::Continue(())
46 ///     }
47 /// }
48 /// ```
49 #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
50 #[derive(Debug, Clone, Copy, PartialEq)]
51 pub enum ControlFlow<B, C = ()> {
52     /// Move on to the next phase of the operation as normal.
53     #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
54     #[lang = "Continue"]
55     Continue(C),
56     /// Exit the operation without running subsequent phases.
57     #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
58     #[lang = "Break"]
59     Break(B),
60     // Yes, the order of the variants doesn't match the type parameters.
61     // They're in this order so that `ControlFlow<A, B>` <-> `Result<B, A>`
62     // is a no-op conversion in the `Try` implementation.
63 }
64
65 #[unstable(feature = "try_trait_v2", issue = "84277")]
66 impl<B, C> ops::Try for ControlFlow<B, C> {
67     type Output = C;
68     type Residual = ControlFlow<B, convert::Infallible>;
69
70     #[inline]
71     fn from_output(output: Self::Output) -> Self {
72         ControlFlow::Continue(output)
73     }
74
75     #[inline]
76     fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
77         match self {
78             ControlFlow::Continue(c) => ControlFlow::Continue(c),
79             ControlFlow::Break(b) => ControlFlow::Break(ControlFlow::Break(b)),
80         }
81     }
82 }
83
84 #[unstable(feature = "try_trait_v2", issue = "84277")]
85 impl<B, C> ops::FromResidual for ControlFlow<B, C> {
86     #[inline]
87     fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {
88         match residual {
89             ControlFlow::Break(b) => ControlFlow::Break(b),
90         }
91     }
92 }
93
94 impl<B, C> ControlFlow<B, C> {
95     /// Returns `true` if this is a `Break` variant.
96     ///
97     /// # Examples
98     ///
99     /// ```
100     /// #![feature(control_flow_enum)]
101     /// use std::ops::ControlFlow;
102     ///
103     /// assert!(ControlFlow::<i32, String>::Break(3).is_break());
104     /// assert!(!ControlFlow::<String, i32>::Continue(3).is_break());
105     /// ```
106     #[inline]
107     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
108     pub fn is_break(&self) -> bool {
109         matches!(*self, ControlFlow::Break(_))
110     }
111
112     /// Returns `true` if this is a `Continue` variant.
113     ///
114     /// # Examples
115     ///
116     /// ```
117     /// #![feature(control_flow_enum)]
118     /// use std::ops::ControlFlow;
119     ///
120     /// assert!(!ControlFlow::<i32, String>::Break(3).is_continue());
121     /// assert!(ControlFlow::<String, i32>::Continue(3).is_continue());
122     /// ```
123     #[inline]
124     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
125     pub fn is_continue(&self) -> bool {
126         matches!(*self, ControlFlow::Continue(_))
127     }
128
129     /// Converts the `ControlFlow` into an `Option` which is `Some` if the
130     /// `ControlFlow` was `Break` and `None` otherwise.
131     ///
132     /// # Examples
133     ///
134     /// ```
135     /// #![feature(control_flow_enum)]
136     /// use std::ops::ControlFlow;
137     ///
138     /// assert_eq!(ControlFlow::<i32, String>::Break(3).break_value(), Some(3));
139     /// assert_eq!(ControlFlow::<String, i32>::Continue(3).break_value(), None);
140     /// ```
141     #[inline]
142     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
143     pub fn break_value(self) -> Option<B> {
144         match self {
145             ControlFlow::Continue(..) => None,
146             ControlFlow::Break(x) => Some(x),
147         }
148     }
149
150     /// Maps `ControlFlow<B, C>` to `ControlFlow<T, C>` by applying a function
151     /// to the break value in case it exists.
152     #[inline]
153     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
154     pub fn map_break<T, F>(self, f: F) -> ControlFlow<T, C>
155     where
156         F: FnOnce(B) -> T,
157     {
158         match self {
159             ControlFlow::Continue(x) => ControlFlow::Continue(x),
160             ControlFlow::Break(x) => ControlFlow::Break(f(x)),
161         }
162     }
163 }
164
165 /// These are used only as part of implementing the iterator adapters.
166 /// They have mediocre names and non-obvious semantics, so aren't
167 /// currently on a path to potential stabilization.
168 impl<R: ops::Try> ControlFlow<R, R::Output> {
169     /// Create a `ControlFlow` from any type implementing `Try`.
170     #[inline]
171     pub(crate) fn from_try(r: R) -> Self {
172         match R::branch(r) {
173             ControlFlow::Continue(v) => ControlFlow::Continue(v),
174             ControlFlow::Break(v) => ControlFlow::Break(R::from_residual(v)),
175         }
176     }
177
178     /// Convert a `ControlFlow` into any type implementing `Try`;
179     #[inline]
180     pub(crate) fn into_try(self) -> R {
181         match self {
182             ControlFlow::Continue(v) => R::from_output(v),
183             ControlFlow::Break(v) => v,
184         }
185     }
186 }
187
188 impl<B> ControlFlow<B, ()> {
189     /// It's frequently the case that there's no value needed with `Continue`,
190     /// so this provides a way to avoid typing `(())`, if you prefer it.
191     ///
192     /// # Examples
193     ///
194     /// ```
195     /// #![feature(control_flow_enum)]
196     /// use std::ops::ControlFlow;
197     ///
198     /// let mut partial_sum = 0;
199     /// let last_used = (1..10).chain(20..25).try_for_each(|x| {
200     ///     partial_sum += x;
201     ///     if partial_sum > 100 { ControlFlow::Break(x) }
202     ///     else { ControlFlow::CONTINUE }
203     /// });
204     /// assert_eq!(last_used.break_value(), Some(22));
205     /// ```
206     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
207     pub const CONTINUE: Self = ControlFlow::Continue(());
208 }
209
210 impl<C> ControlFlow<(), C> {
211     /// APIs like `try_for_each` don't need values with `Break`,
212     /// so this provides a way to avoid typing `(())`, if you prefer it.
213     ///
214     /// # Examples
215     ///
216     /// ```
217     /// #![feature(control_flow_enum)]
218     /// use std::ops::ControlFlow;
219     ///
220     /// let mut partial_sum = 0;
221     /// (1..10).chain(20..25).try_for_each(|x| {
222     ///     if partial_sum > 100 { ControlFlow::BREAK }
223     ///     else { partial_sum += x; ControlFlow::CONTINUE }
224     /// });
225     /// assert_eq!(partial_sum, 108);
226     /// ```
227     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
228     pub const BREAK: Self = ControlFlow::Break(());
229 }