]> git.lizzy.rs Git - rust.git/blob - library/core/src/ops/control_flow.rs
Auto merge of #85376 - RalfJung:ptrless-allocs, r=oli-obk
[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 /// #![feature(control_flow_enum)]
15 /// use std::ops::ControlFlow;
16 ///
17 /// let r = (2..100).try_for_each(|x| {
18 ///     if 403 % x == 0 {
19 ///         return ControlFlow::Break(x)
20 ///     }
21 ///
22 ///     ControlFlow::Continue(())
23 /// });
24 /// assert_eq!(r, ControlFlow::Break(13));
25 /// ```
26 ///
27 /// A basic tree traversal:
28 /// ```no_run
29 /// #![feature(control_flow_enum)]
30 /// use std::ops::ControlFlow;
31 ///
32 /// pub struct TreeNode<T> {
33 ///     value: T,
34 ///     left: Option<Box<TreeNode<T>>>,
35 ///     right: Option<Box<TreeNode<T>>>,
36 /// }
37 ///
38 /// impl<T> TreeNode<T> {
39 ///     pub fn traverse_inorder<B>(&self, mut f: impl FnMut(&T) -> ControlFlow<B>) -> ControlFlow<B> {
40 ///         if let Some(left) = &self.left {
41 ///             left.traverse_inorder(&mut f)?;
42 ///         }
43 ///         f(&self.value)?;
44 ///         if let Some(right) = &self.right {
45 ///             right.traverse_inorder(&mut f)?;
46 ///         }
47 ///         ControlFlow::Continue(())
48 ///     }
49 /// }
50 /// ```
51 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
52 #[derive(Debug, Clone, Copy, PartialEq)]
53 pub enum ControlFlow<B, C = ()> {
54     /// Move on to the next phase of the operation as normal.
55     #[cfg_attr(not(bootstrap), lang = "Continue")]
56     Continue(C),
57     /// Exit the operation without running subsequent phases.
58     #[cfg_attr(not(bootstrap), 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 = "control_flow_enum", reason = "new API", issue = "75744")]
66 impl<B, C> ops::TryV1 for ControlFlow<B, C> {
67     type Output = C;
68     type Error = B;
69     #[inline]
70     fn into_result(self) -> Result<Self::Output, Self::Error> {
71         match self {
72             ControlFlow::Continue(y) => Ok(y),
73             ControlFlow::Break(x) => Err(x),
74         }
75     }
76     #[inline]
77     fn from_error(v: Self::Error) -> Self {
78         ControlFlow::Break(v)
79     }
80     #[inline]
81     fn from_ok(v: Self::Output) -> Self {
82         ControlFlow::Continue(v)
83     }
84 }
85
86 #[unstable(feature = "try_trait_v2", issue = "84277")]
87 impl<B, C> ops::TryV2 for ControlFlow<B, C> {
88     type Output = C;
89     type Residual = ControlFlow<B, convert::Infallible>;
90
91     #[inline]
92     fn from_output(output: Self::Output) -> Self {
93         ControlFlow::Continue(output)
94     }
95
96     #[inline]
97     fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
98         match self {
99             ControlFlow::Continue(c) => ControlFlow::Continue(c),
100             ControlFlow::Break(b) => ControlFlow::Break(ControlFlow::Break(b)),
101         }
102     }
103 }
104
105 #[unstable(feature = "try_trait_v2", issue = "84277")]
106 impl<B, C> ops::FromResidual for ControlFlow<B, C> {
107     #[inline]
108     fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {
109         match residual {
110             ControlFlow::Break(b) => ControlFlow::Break(b),
111         }
112     }
113 }
114
115 impl<B, C> ControlFlow<B, C> {
116     /// Returns `true` if this is a `Break` variant.
117     ///
118     /// # Examples
119     ///
120     /// ```
121     /// #![feature(control_flow_enum)]
122     /// use std::ops::ControlFlow;
123     ///
124     /// assert!(ControlFlow::<i32, String>::Break(3).is_break());
125     /// assert!(!ControlFlow::<String, i32>::Continue(3).is_break());
126     /// ```
127     #[inline]
128     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
129     pub fn is_break(&self) -> bool {
130         matches!(*self, ControlFlow::Break(_))
131     }
132
133     /// Returns `true` if this is a `Continue` variant.
134     ///
135     /// # Examples
136     ///
137     /// ```
138     /// #![feature(control_flow_enum)]
139     /// use std::ops::ControlFlow;
140     ///
141     /// assert!(!ControlFlow::<i32, String>::Break(3).is_continue());
142     /// assert!(ControlFlow::<String, i32>::Continue(3).is_continue());
143     /// ```
144     #[inline]
145     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
146     pub fn is_continue(&self) -> bool {
147         matches!(*self, ControlFlow::Continue(_))
148     }
149
150     /// Converts the `ControlFlow` into an `Option` which is `Some` if the
151     /// `ControlFlow` was `Break` and `None` otherwise.
152     ///
153     /// # Examples
154     ///
155     /// ```
156     /// #![feature(control_flow_enum)]
157     /// use std::ops::ControlFlow;
158     ///
159     /// assert_eq!(ControlFlow::<i32, String>::Break(3).break_value(), Some(3));
160     /// assert_eq!(ControlFlow::<String, i32>::Continue(3).break_value(), None);
161     /// ```
162     #[inline]
163     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
164     pub fn break_value(self) -> Option<B> {
165         match self {
166             ControlFlow::Continue(..) => None,
167             ControlFlow::Break(x) => Some(x),
168         }
169     }
170
171     /// Maps `ControlFlow<B, C>` to `ControlFlow<T, C>` by applying a function
172     /// to the break value in case it exists.
173     #[inline]
174     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
175     pub fn map_break<T, F>(self, f: F) -> ControlFlow<T, C>
176     where
177         F: FnOnce(B) -> T,
178     {
179         match self {
180             ControlFlow::Continue(x) => ControlFlow::Continue(x),
181             ControlFlow::Break(x) => ControlFlow::Break(f(x)),
182         }
183     }
184 }
185
186 #[cfg(bootstrap)]
187 impl<R: ops::TryV1> ControlFlow<R, R::Output> {
188     /// Create a `ControlFlow` from any type implementing `Try`.
189     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
190     #[inline]
191     pub fn from_try(r: R) -> Self {
192         match R::into_result(r) {
193             Ok(v) => ControlFlow::Continue(v),
194             Err(v) => ControlFlow::Break(R::from_error(v)),
195         }
196     }
197
198     /// Convert a `ControlFlow` into any type implementing `Try`;
199     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
200     #[inline]
201     pub fn into_try(self) -> R {
202         match self {
203             ControlFlow::Continue(v) => R::from_ok(v),
204             ControlFlow::Break(v) => v,
205         }
206     }
207 }
208
209 #[cfg(not(bootstrap))]
210 impl<R: ops::TryV2> ControlFlow<R, R::Output> {
211     /// Create a `ControlFlow` from any type implementing `Try`.
212     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
213     #[inline]
214     pub fn from_try(r: R) -> Self {
215         match R::branch(r) {
216             ControlFlow::Continue(v) => ControlFlow::Continue(v),
217             ControlFlow::Break(v) => ControlFlow::Break(R::from_residual(v)),
218         }
219     }
220
221     /// Convert a `ControlFlow` into any type implementing `Try`;
222     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
223     #[inline]
224     pub fn into_try(self) -> R {
225         match self {
226             ControlFlow::Continue(v) => R::from_output(v),
227             ControlFlow::Break(v) => v,
228         }
229     }
230 }
231
232 impl<B> ControlFlow<B, ()> {
233     /// It's frequently the case that there's no value needed with `Continue`,
234     /// so this provides a way to avoid typing `(())`, if you prefer it.
235     ///
236     /// # Examples
237     ///
238     /// ```
239     /// #![feature(control_flow_enum)]
240     /// use std::ops::ControlFlow;
241     ///
242     /// let mut partial_sum = 0;
243     /// let last_used = (1..10).chain(20..25).try_for_each(|x| {
244     ///     partial_sum += x;
245     ///     if partial_sum > 100 { ControlFlow::Break(x) }
246     ///     else { ControlFlow::CONTINUE }
247     /// });
248     /// assert_eq!(last_used.break_value(), Some(22));
249     /// ```
250     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
251     pub const CONTINUE: Self = ControlFlow::Continue(());
252 }
253
254 impl<C> ControlFlow<(), C> {
255     /// APIs like `try_for_each` don't need values with `Break`,
256     /// so this provides a way to avoid typing `(())`, if you prefer it.
257     ///
258     /// # Examples
259     ///
260     /// ```
261     /// #![feature(control_flow_enum)]
262     /// use std::ops::ControlFlow;
263     ///
264     /// let mut partial_sum = 0;
265     /// (1..10).chain(20..25).try_for_each(|x| {
266     ///     if partial_sum > 100 { ControlFlow::BREAK }
267     ///     else { partial_sum += x; ControlFlow::CONTINUE }
268     /// });
269     /// assert_eq!(partial_sum, 108);
270     /// ```
271     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
272     pub const BREAK: Self = ControlFlow::Break(());
273 }