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