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