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", old_name = "try_trait")]
102#[rustc_const_unstable(feature = "const_try", issue = "74935")]
103impl<B, C> const ops::Try for ControlFlow<B, C> {
104 type Output = C;
105 type Residual = ControlFlow<B, convert::Infallible>;
106
107 #[inline]
108 fn from_output(output: Self::Output) -> Self {
109 ControlFlow::Continue(output)
110 }
111
112 #[inline]
113 fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
114 match self {
115 ControlFlow::Continue(c) => ControlFlow::Continue(c),
116 ControlFlow::Break(b) => ControlFlow::Break(ControlFlow::Break(b)),
117 }
118 }
119}
120
121#[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
122#[rustc_const_unstable(feature = "const_try", issue = "74935")]
123// Note: manually specifying the residual type instead of using the default to work around
124// https://github.com/rust-lang/rust/issues/99940
125impl<B, C> const ops::FromResidual<ControlFlow<B, convert::Infallible>> for ControlFlow<B, C> {
126 #[inline]
127 fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {
128 match residual {
129 ControlFlow::Break(b) => ControlFlow::Break(b),
130 }
131 }
132}
133
134#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
135impl<B, C> ops::Residual<C> for ControlFlow<B, convert::Infallible> {
136 type TryType = ControlFlow<B, C>;
137}
138
139impl<B, C> ControlFlow<B, C> {
140 /// Returns `true` if this is a `Break` variant.
141 ///
142 /// # Examples
143 ///
144 /// ```
145 /// use std::ops::ControlFlow;
146 ///
147 /// assert!(ControlFlow::<&str, i32>::Break("Stop right there!").is_break());
148 /// assert!(!ControlFlow::<&str, i32>::Continue(3).is_break());
149 /// ```
150 #[inline]
151 #[stable(feature = "control_flow_enum_is", since = "1.59.0")]
152 pub fn is_break(&self) -> bool {
153 matches!(*self, ControlFlow::Break(_))
154 }
155
156 /// Returns `true` if this is a `Continue` variant.
157 ///
158 /// # Examples
159 ///
160 /// ```
161 /// use std::ops::ControlFlow;
162 ///
163 /// assert!(!ControlFlow::<&str, i32>::Break("Stop right there!").is_continue());
164 /// assert!(ControlFlow::<&str, i32>::Continue(3).is_continue());
165 /// ```
166 #[inline]
167 #[stable(feature = "control_flow_enum_is", since = "1.59.0")]
168 pub fn is_continue(&self) -> bool {
169 matches!(*self, ControlFlow::Continue(_))
170 }
171
172 /// Converts the `ControlFlow` into an `Option` which is `Some` if the
173 /// `ControlFlow` was `Break` and `None` otherwise.
174 ///
175 /// # Examples
176 ///
177 /// ```
178 /// use std::ops::ControlFlow;
179 ///
180 /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").break_value(), Some("Stop right there!"));
181 /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).break_value(), None);
182 /// ```
183 #[inline]
184 #[stable(feature = "control_flow_enum", since = "1.83.0")]
185 pub fn break_value(self) -> Option<B> {
186 match self {
187 ControlFlow::Continue(..) => None,
188 ControlFlow::Break(x) => Some(x),
189 }
190 }
191
192 /// Converts the `ControlFlow` into an `Result` which is `Ok` if the
193 /// `ControlFlow` was `Break` and `Err` if otherwise.
194 ///
195 /// # Examples
196 ///
197 /// ```
198 /// #![feature(control_flow_ok)]
199 ///
200 /// use std::ops::ControlFlow;
201 ///
202 /// struct TreeNode<T> {
203 /// value: T,
204 /// left: Option<Box<TreeNode<T>>>,
205 /// right: Option<Box<TreeNode<T>>>,
206 /// }
207 ///
208 /// impl<T> TreeNode<T> {
209 /// fn find<'a>(&'a self, mut predicate: impl FnMut(&T) -> bool) -> Result<&'a T, ()> {
210 /// let mut f = |t: &'a T| -> ControlFlow<&'a T> {
211 /// if predicate(t) {
212 /// ControlFlow::Break(t)
213 /// } else {
214 /// ControlFlow::Continue(())
215 /// }
216 /// };
217 ///
218 /// self.traverse_inorder(&mut f).break_ok()
219 /// }
220 ///
221 /// fn traverse_inorder<'a, B>(
222 /// &'a self,
223 /// f: &mut impl FnMut(&'a T) -> ControlFlow<B>,
224 /// ) -> ControlFlow<B> {
225 /// if let Some(left) = &self.left {
226 /// left.traverse_inorder(f)?;
227 /// }
228 /// f(&self.value)?;
229 /// if let Some(right) = &self.right {
230 /// right.traverse_inorder(f)?;
231 /// }
232 /// ControlFlow::Continue(())
233 /// }
234 ///
235 /// fn leaf(value: T) -> Option<Box<TreeNode<T>>> {
236 /// Some(Box::new(Self {
237 /// value,
238 /// left: None,
239 /// right: None,
240 /// }))
241 /// }
242 /// }
243 ///
244 /// let node = TreeNode {
245 /// value: 0,
246 /// left: TreeNode::leaf(1),
247 /// right: Some(Box::new(TreeNode {
248 /// value: -1,
249 /// left: TreeNode::leaf(5),
250 /// right: TreeNode::leaf(2),
251 /// })),
252 /// };
253 ///
254 /// let res = node.find(|val: &i32| *val > 3);
255 /// assert_eq!(res, Ok(&5));
256 /// ```
257 #[inline]
258 #[unstable(feature = "control_flow_ok", issue = "140266")]
259 pub fn break_ok(self) -> Result<B, C> {
260 match self {
261 ControlFlow::Continue(c) => Err(c),
262 ControlFlow::Break(b) => Ok(b),
263 }
264 }
265
266 /// Maps `ControlFlow<B, C>` to `ControlFlow<T, C>` by applying a function
267 /// to the break value in case it exists.
268 #[inline]
269 #[stable(feature = "control_flow_enum", since = "1.83.0")]
270 pub fn map_break<T>(self, f: impl FnOnce(B) -> T) -> ControlFlow<T, C> {
271 match self {
272 ControlFlow::Continue(x) => ControlFlow::Continue(x),
273 ControlFlow::Break(x) => ControlFlow::Break(f(x)),
274 }
275 }
276
277 /// Converts the `ControlFlow` into an `Option` which is `Some` if the
278 /// `ControlFlow` was `Continue` and `None` otherwise.
279 ///
280 /// # Examples
281 ///
282 /// ```
283 /// use std::ops::ControlFlow;
284 ///
285 /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").continue_value(), None);
286 /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).continue_value(), Some(3));
287 /// ```
288 #[inline]
289 #[stable(feature = "control_flow_enum", since = "1.83.0")]
290 pub fn continue_value(self) -> Option<C> {
291 match self {
292 ControlFlow::Continue(x) => Some(x),
293 ControlFlow::Break(..) => None,
294 }
295 }
296
297 /// Converts the `ControlFlow` into an `Result` which is `Ok` if the
298 /// `ControlFlow` was `Continue` and `Err` if otherwise.
299 ///
300 /// # Examples
301 ///
302 /// ```
303 /// #![feature(control_flow_ok)]
304 ///
305 /// use std::ops::ControlFlow;
306 ///
307 /// struct TreeNode<T> {
308 /// value: T,
309 /// left: Option<Box<TreeNode<T>>>,
310 /// right: Option<Box<TreeNode<T>>>,
311 /// }
312 ///
313 /// impl<T> TreeNode<T> {
314 /// fn validate<B>(&self, f: &mut impl FnMut(&T) -> ControlFlow<B>) -> Result<(), B> {
315 /// self.traverse_inorder(f).continue_ok()
316 /// }
317 ///
318 /// fn traverse_inorder<B>(&self, f: &mut impl FnMut(&T) -> ControlFlow<B>) -> ControlFlow<B> {
319 /// if let Some(left) = &self.left {
320 /// left.traverse_inorder(f)?;
321 /// }
322 /// f(&self.value)?;
323 /// if let Some(right) = &self.right {
324 /// right.traverse_inorder(f)?;
325 /// }
326 /// ControlFlow::Continue(())
327 /// }
328 ///
329 /// fn leaf(value: T) -> Option<Box<TreeNode<T>>> {
330 /// Some(Box::new(Self {
331 /// value,
332 /// left: None,
333 /// right: None,
334 /// }))
335 /// }
336 /// }
337 ///
338 /// let node = TreeNode {
339 /// value: 0,
340 /// left: TreeNode::leaf(1),
341 /// right: Some(Box::new(TreeNode {
342 /// value: -1,
343 /// left: TreeNode::leaf(5),
344 /// right: TreeNode::leaf(2),
345 /// })),
346 /// };
347 ///
348 /// let res = node.validate(&mut |val| {
349 /// if *val < 0 {
350 /// return ControlFlow::Break("negative value detected");
351 /// }
352 ///
353 /// if *val > 4 {
354 /// return ControlFlow::Break("too big value detected");
355 /// }
356 ///
357 /// ControlFlow::Continue(())
358 /// });
359 /// assert_eq!(res, Err("too big value detected"));
360 /// ```
361 #[inline]
362 #[unstable(feature = "control_flow_ok", issue = "140266")]
363 pub fn continue_ok(self) -> Result<C, B> {
364 match self {
365 ControlFlow::Continue(c) => Ok(c),
366 ControlFlow::Break(b) => Err(b),
367 }
368 }
369
370 /// Maps `ControlFlow<B, C>` to `ControlFlow<B, T>` by applying a function
371 /// to the continue value in case it exists.
372 #[inline]
373 #[stable(feature = "control_flow_enum", since = "1.83.0")]
374 pub fn map_continue<T>(self, f: impl FnOnce(C) -> T) -> ControlFlow<B, T> {
375 match self {
376 ControlFlow::Continue(x) => ControlFlow::Continue(f(x)),
377 ControlFlow::Break(x) => ControlFlow::Break(x),
378 }
379 }
380}
381
382impl<T> ControlFlow<T, T> {
383 /// Extracts the value `T` that is wrapped by `ControlFlow<T, T>`.
384 ///
385 /// # Examples
386 ///
387 /// ```
388 /// #![feature(control_flow_into_value)]
389 /// use std::ops::ControlFlow;
390 ///
391 /// assert_eq!(ControlFlow::<i32, i32>::Break(1024).into_value(), 1024);
392 /// assert_eq!(ControlFlow::<i32, i32>::Continue(512).into_value(), 512);
393 /// ```
394 #[unstable(feature = "control_flow_into_value", issue = "137461")]
395 #[rustc_allow_const_fn_unstable(const_precise_live_drops)]
396 pub const fn into_value(self) -> T {
397 match self {
398 ControlFlow::Continue(x) | ControlFlow::Break(x) => x,
399 }
400 }
401}
402
403/// These are used only as part of implementing the iterator adapters.
404/// They have mediocre names and non-obvious semantics, so aren't
405/// currently on a path to potential stabilization.
406impl<R: ops::Try> ControlFlow<R, R::Output> {
407 /// Creates a `ControlFlow` from any type implementing `Try`.
408 #[inline]
409 pub(crate) fn from_try(r: R) -> Self {
410 match R::branch(r) {
411 ControlFlow::Continue(v) => ControlFlow::Continue(v),
412 ControlFlow::Break(v) => ControlFlow::Break(R::from_residual(v)),
413 }
414 }
415
416 /// Converts a `ControlFlow` into any type implementing `Try`.
417 #[inline]
418 pub(crate) fn into_try(self) -> R {
419 match self {
420 ControlFlow::Continue(v) => R::from_output(v),
421 ControlFlow::Break(v) => v,
422 }
423 }
424}