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