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