core/ops/control_flow.rs
1use crate::marker::Destruct;
2use crate::{convert, ops};
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/// Similar to [`Option`] and [`Result`], this enum can be used with the `?` operator
12/// to return immediately if the [`Break`] variant is present or otherwise continue normally
13/// with the value inside the [`Continue`] variant.
14///
15/// # Examples
16///
17/// Early-exiting from [`Iterator::try_for_each`]:
18/// ```
19/// use std::ops::ControlFlow;
20///
21/// let r = (2..100).try_for_each(|x| {
22/// if 403 % x == 0 {
23/// return ControlFlow::Break(x)
24/// }
25///
26/// ControlFlow::Continue(())
27/// });
28/// assert_eq!(r, ControlFlow::Break(13));
29/// ```
30///
31/// A basic tree traversal:
32/// ```
33/// use std::ops::ControlFlow;
34///
35/// pub struct TreeNode<T> {
36/// value: T,
37/// left: Option<Box<TreeNode<T>>>,
38/// right: Option<Box<TreeNode<T>>>,
39/// }
40///
41/// impl<T> TreeNode<T> {
42/// pub fn traverse_inorder<B>(&self, f: &mut impl FnMut(&T) -> ControlFlow<B>) -> ControlFlow<B> {
43/// if let Some(left) = &self.left {
44/// left.traverse_inorder(f)?;
45/// }
46/// f(&self.value)?;
47/// if let Some(right) = &self.right {
48/// right.traverse_inorder(f)?;
49/// }
50/// ControlFlow::Continue(())
51/// }
52/// fn leaf(value: T) -> Option<Box<TreeNode<T>>> {
53/// Some(Box::new(Self { value, left: None, right: None }))
54/// }
55/// }
56///
57/// let node = TreeNode {
58/// value: 0,
59/// left: TreeNode::leaf(1),
60/// right: Some(Box::new(TreeNode {
61/// value: -1,
62/// left: TreeNode::leaf(5),
63/// right: TreeNode::leaf(2),
64/// }))
65/// };
66/// let mut sum = 0;
67///
68/// let res = node.traverse_inorder(&mut |val| {
69/// if *val < 0 {
70/// ControlFlow::Break(*val)
71/// } else {
72/// sum += *val;
73/// ControlFlow::Continue(())
74/// }
75/// });
76/// assert_eq!(res, ControlFlow::Break(-1));
77/// assert_eq!(sum, 6);
78/// ```
79///
80/// [`Break`]: ControlFlow::Break
81/// [`Continue`]: ControlFlow::Continue
82#[stable(feature = "control_flow_enum_type", since = "1.55.0")]
83#[rustc_diagnostic_item = "ControlFlow"]
84#[must_use]
85// ControlFlow should not implement PartialOrd or Ord, per RFC 3058:
86// https://rust-lang.github.io/rfcs/3058-try-trait-v2.html#traits-for-controlflow
87#[derive(Copy, Debug, Hash)]
88#[derive_const(Clone, PartialEq, Eq)]
89pub enum ControlFlow<B, C = ()> {
90 /// Move on to the next phase of the operation as normal.
91 #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
92 #[lang = "Continue"]
93 Continue(C),
94 /// Exit the operation without running subsequent phases.
95 #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
96 #[lang = "Break"]
97 Break(B),
98 // Yes, the order of the variants doesn't match the type parameters.
99 // They're in this order so that `ControlFlow<A, B>` <-> `Result<B, A>`
100 // is a no-op conversion in the `Try` implementation.
101}
102
103#[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
104#[rustc_const_unstable(feature = "const_try", issue = "74935")]
105impl<B, C> const ops::Try for ControlFlow<B, C> {
106 type Output = C;
107 type Residual = ControlFlow<B, convert::Infallible>;
108
109 #[inline]
110 fn from_output(output: Self::Output) -> Self {
111 ControlFlow::Continue(output)
112 }
113
114 #[inline]
115 fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
116 match self {
117 ControlFlow::Continue(c) => ControlFlow::Continue(c),
118 ControlFlow::Break(b) => ControlFlow::Break(ControlFlow::Break(b)),
119 }
120 }
121}
122
123#[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
124#[rustc_const_unstable(feature = "const_try", issue = "74935")]
125// Note: manually specifying the residual type instead of using the default to work around
126// https://github.com/rust-lang/rust/issues/99940
127impl<B, C> const ops::FromResidual<ControlFlow<B, convert::Infallible>> for ControlFlow<B, C> {
128 #[inline]
129 fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {
130 match residual {
131 ControlFlow::Break(b) => ControlFlow::Break(b),
132 }
133 }
134}
135
136#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
137#[rustc_const_unstable(feature = "const_try_residual", issue = "91285")]
138impl<B, C> const ops::Residual<C> for ControlFlow<B, convert::Infallible> {
139 type TryType = ControlFlow<B, C>;
140}
141
142impl<B, C> ControlFlow<B, C> {
143 /// Returns `true` if this is a `Break` variant.
144 ///
145 /// # Examples
146 ///
147 /// ```
148 /// use std::ops::ControlFlow;
149 ///
150 /// assert!(ControlFlow::<&str, i32>::Break("Stop right there!").is_break());
151 /// assert!(!ControlFlow::<&str, i32>::Continue(3).is_break());
152 /// ```
153 #[inline]
154 #[stable(feature = "control_flow_enum_is", since = "1.59.0")]
155 #[rustc_const_stable(feature = "min_const_control_flow", since = "1.95.0")]
156 pub const fn is_break(&self) -> bool {
157 matches!(*self, ControlFlow::Break(_))
158 }
159
160 /// Returns `true` if this is a `Continue` variant.
161 ///
162 /// # Examples
163 ///
164 /// ```
165 /// use std::ops::ControlFlow;
166 ///
167 /// assert!(!ControlFlow::<&str, i32>::Break("Stop right there!").is_continue());
168 /// assert!(ControlFlow::<&str, i32>::Continue(3).is_continue());
169 /// ```
170 #[inline]
171 #[stable(feature = "control_flow_enum_is", since = "1.59.0")]
172 #[rustc_const_stable(feature = "min_const_control_flow", since = "1.95.0")]
173 pub const fn is_continue(&self) -> bool {
174 matches!(*self, ControlFlow::Continue(_))
175 }
176
177 /// Converts the `ControlFlow` into an `Option` which is `Some` if the
178 /// `ControlFlow` was `Break` and `None` otherwise.
179 ///
180 /// # Examples
181 ///
182 /// ```
183 /// use std::ops::ControlFlow;
184 ///
185 /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").break_value(), Some("Stop right there!"));
186 /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).break_value(), None);
187 /// ```
188 #[inline]
189 #[stable(feature = "control_flow_enum", since = "1.83.0")]
190 #[rustc_const_unstable(feature = "const_control_flow", issue = "148739")]
191 pub const fn break_value(self) -> Option<B>
192 where
193 Self: [const] Destruct,
194 {
195 match self {
196 ControlFlow::Continue(..) => None,
197 ControlFlow::Break(x) => Some(x),
198 }
199 }
200
201 /// Converts the `ControlFlow` into a `Result` which is `Ok` if the
202 /// `ControlFlow` was `Break` and `Err` if otherwise.
203 ///
204 /// # Examples
205 ///
206 /// ```
207 /// use std::ops::ControlFlow;
208 ///
209 /// struct TreeNode<T> {
210 /// value: T,
211 /// left: Option<Box<TreeNode<T>>>,
212 /// right: Option<Box<TreeNode<T>>>,
213 /// }
214 ///
215 /// impl<T> TreeNode<T> {
216 /// fn find<'a>(&'a self, mut predicate: impl FnMut(&T) -> bool) -> Result<&'a T, ()> {
217 /// let mut f = |t: &'a T| -> ControlFlow<&'a T> {
218 /// if predicate(t) {
219 /// ControlFlow::Break(t)
220 /// } else {
221 /// ControlFlow::Continue(())
222 /// }
223 /// };
224 ///
225 /// self.traverse_inorder(&mut f).break_ok()
226 /// }
227 ///
228 /// fn traverse_inorder<'a, B>(
229 /// &'a self,
230 /// f: &mut impl FnMut(&'a T) -> ControlFlow<B>,
231 /// ) -> ControlFlow<B> {
232 /// if let Some(left) = &self.left {
233 /// left.traverse_inorder(f)?;
234 /// }
235 /// f(&self.value)?;
236 /// if let Some(right) = &self.right {
237 /// right.traverse_inorder(f)?;
238 /// }
239 /// ControlFlow::Continue(())
240 /// }
241 ///
242 /// fn leaf(value: T) -> Option<Box<TreeNode<T>>> {
243 /// Some(Box::new(Self {
244 /// value,
245 /// left: None,
246 /// right: None,
247 /// }))
248 /// }
249 /// }
250 ///
251 /// let node = TreeNode {
252 /// value: 0,
253 /// left: TreeNode::leaf(1),
254 /// right: Some(Box::new(TreeNode {
255 /// value: -1,
256 /// left: TreeNode::leaf(5),
257 /// right: TreeNode::leaf(2),
258 /// })),
259 /// };
260 ///
261 /// let res = node.find(|val: &i32| *val > 3);
262 /// assert_eq!(res, Ok(&5));
263 /// ```
264 #[inline]
265 #[stable(feature = "control_flow_ok", since = "1.96.0")]
266 #[rustc_const_stable(feature = "control_flow_ok", since = "1.96.0")]
267 #[rustc_allow_const_fn_unstable(const_precise_live_drops)]
268 pub const fn break_ok(self) -> Result<B, C> {
269 match self {
270 ControlFlow::Continue(c) => Err(c),
271 ControlFlow::Break(b) => Ok(b),
272 }
273 }
274
275 /// Maps `ControlFlow<B, C>` to `ControlFlow<T, C>` by applying a function
276 /// to the break value in case it exists.
277 #[inline]
278 #[stable(feature = "control_flow_enum", since = "1.83.0")]
279 #[rustc_const_unstable(feature = "const_control_flow", issue = "148739")]
280 pub const fn map_break<T, F>(self, f: F) -> ControlFlow<T, C>
281 where
282 F: [const] FnOnce(B) -> T + [const] Destruct,
283 {
284 match self {
285 ControlFlow::Continue(x) => ControlFlow::Continue(x),
286 ControlFlow::Break(x) => ControlFlow::Break(f(x)),
287 }
288 }
289
290 /// Converts the `ControlFlow` into an `Option` which is `Some` if the
291 /// `ControlFlow` was `Continue` and `None` otherwise.
292 ///
293 /// # Examples
294 ///
295 /// ```
296 /// use std::ops::ControlFlow;
297 ///
298 /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").continue_value(), None);
299 /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).continue_value(), Some(3));
300 /// ```
301 #[inline]
302 #[stable(feature = "control_flow_enum", since = "1.83.0")]
303 #[rustc_const_unstable(feature = "const_control_flow", issue = "148739")]
304 pub const fn continue_value(self) -> Option<C>
305 where
306 Self: [const] Destruct,
307 {
308 match self {
309 ControlFlow::Continue(x) => Some(x),
310 ControlFlow::Break(..) => None,
311 }
312 }
313
314 /// Converts the `ControlFlow` into a `Result` which is `Ok` if the
315 /// `ControlFlow` was `Continue` and `Err` if otherwise.
316 ///
317 /// # Examples
318 ///
319 /// ```
320 /// use std::ops::ControlFlow;
321 ///
322 /// struct TreeNode<T> {
323 /// value: T,
324 /// left: Option<Box<TreeNode<T>>>,
325 /// right: Option<Box<TreeNode<T>>>,
326 /// }
327 ///
328 /// impl<T> TreeNode<T> {
329 /// fn validate<B>(&self, f: &mut impl FnMut(&T) -> ControlFlow<B>) -> Result<(), B> {
330 /// self.traverse_inorder(f).continue_ok()
331 /// }
332 ///
333 /// fn traverse_inorder<B>(&self, f: &mut impl FnMut(&T) -> ControlFlow<B>) -> ControlFlow<B> {
334 /// if let Some(left) = &self.left {
335 /// left.traverse_inorder(f)?;
336 /// }
337 /// f(&self.value)?;
338 /// if let Some(right) = &self.right {
339 /// right.traverse_inorder(f)?;
340 /// }
341 /// ControlFlow::Continue(())
342 /// }
343 ///
344 /// fn leaf(value: T) -> Option<Box<TreeNode<T>>> {
345 /// Some(Box::new(Self {
346 /// value,
347 /// left: None,
348 /// right: None,
349 /// }))
350 /// }
351 /// }
352 ///
353 /// let node = TreeNode {
354 /// value: 0,
355 /// left: TreeNode::leaf(1),
356 /// right: Some(Box::new(TreeNode {
357 /// value: -1,
358 /// left: TreeNode::leaf(5),
359 /// right: TreeNode::leaf(2),
360 /// })),
361 /// };
362 ///
363 /// let res = node.validate(&mut |val| {
364 /// if *val < 0 {
365 /// return ControlFlow::Break("negative value detected");
366 /// }
367 ///
368 /// if *val > 4 {
369 /// return ControlFlow::Break("too big value detected");
370 /// }
371 ///
372 /// ControlFlow::Continue(())
373 /// });
374 /// assert_eq!(res, Err("too big value detected"));
375 /// ```
376 #[inline]
377 #[stable(feature = "control_flow_ok", since = "1.96.0")]
378 #[rustc_const_stable(feature = "control_flow_ok", since = "1.96.0")]
379 #[rustc_allow_const_fn_unstable(const_precise_live_drops)]
380 pub const fn continue_ok(self) -> Result<C, B> {
381 match self {
382 ControlFlow::Continue(c) => Ok(c),
383 ControlFlow::Break(b) => Err(b),
384 }
385 }
386
387 /// Maps `ControlFlow<B, C>` to `ControlFlow<B, T>` by applying a function
388 /// to the continue value in case it exists.
389 #[inline]
390 #[stable(feature = "control_flow_enum", since = "1.83.0")]
391 #[rustc_const_unstable(feature = "const_control_flow", issue = "148739")]
392 pub const fn map_continue<T, F>(self, f: F) -> ControlFlow<B, T>
393 where
394 F: [const] FnOnce(C) -> T + [const] Destruct,
395 {
396 match self {
397 ControlFlow::Continue(x) => ControlFlow::Continue(f(x)),
398 ControlFlow::Break(x) => ControlFlow::Break(x),
399 }
400 }
401}
402
403impl<T> ControlFlow<T, T> {
404 /// Extracts the value `T` that is wrapped by `ControlFlow<T, T>`.
405 ///
406 /// # Examples
407 ///
408 /// ```
409 /// #![feature(control_flow_into_value)]
410 /// use std::ops::ControlFlow;
411 ///
412 /// assert_eq!(ControlFlow::<i32, i32>::Break(1024).into_value(), 1024);
413 /// assert_eq!(ControlFlow::<i32, i32>::Continue(512).into_value(), 512);
414 /// ```
415 #[unstable(feature = "control_flow_into_value", issue = "137461")]
416 #[rustc_allow_const_fn_unstable(const_precise_live_drops)]
417 pub const fn into_value(self) -> T {
418 match self {
419 ControlFlow::Continue(x) | ControlFlow::Break(x) => x,
420 }
421 }
422}
423
424// These are used only as part of implementing the iterator adapters.
425// They have mediocre names and non-obvious semantics, so aren't
426// currently on a path to potential stabilization.
427impl<R: ops::Try> ControlFlow<R, R::Output> {
428 /// Creates a `ControlFlow` from any type implementing `Try`.
429 #[inline]
430 pub(crate) fn from_try(r: R) -> Self {
431 match R::branch(r) {
432 ControlFlow::Continue(v) => ControlFlow::Continue(v),
433 ControlFlow::Break(v) => ControlFlow::Break(R::from_residual(v)),
434 }
435 }
436
437 /// Converts a `ControlFlow` into any type implementing `Try`.
438 #[inline]
439 pub(crate) fn into_try(self) -> R {
440 match self {
441 ControlFlow::Continue(v) => R::from_output(v),
442 ControlFlow::Break(v) => v,
443 }
444 }
445}