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