1 use crate::{convert, ops};
3 /// Used to tell an operation whether it should exit early or go on as usual.
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.
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.
16 /// Early-exiting from [`Iterator::try_for_each`]:
18 /// use std::ops::ControlFlow;
20 /// let r = (2..100).try_for_each(|x| {
22 /// return ControlFlow::Break(x)
25 /// ControlFlow::Continue(())
27 /// assert_eq!(r, ControlFlow::Break(13));
30 /// A basic tree traversal:
32 /// use std::ops::ControlFlow;
34 /// pub struct TreeNode<T> {
36 /// left: Option<Box<TreeNode<T>>>,
37 /// right: Option<Box<TreeNode<T>>>,
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)?;
46 /// if let Some(right) = &self.right {
47 /// right.traverse_inorder(f)?;
49 /// ControlFlow::Continue(())
51 /// fn leaf(value: T) -> Option<Box<TreeNode<T>>> {
52 /// Some(Box::new(Self { value, left: None, right: None }))
56 /// let node = TreeNode {
58 /// left: TreeNode::leaf(1),
59 /// right: Some(Box::new(TreeNode {
61 /// left: TreeNode::leaf(5),
62 /// right: TreeNode::leaf(2),
67 /// let res = node.traverse_inorder(&mut |val| {
69 /// ControlFlow::Break(*val)
72 /// ControlFlow::Continue(())
75 /// assert_eq!(res, ControlFlow::Break(-1));
76 /// assert_eq!(sum, 6);
79 /// [`Break`]: ControlFlow::Break
80 /// [`Continue`]: ControlFlow::Continue
81 #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
82 // ControlFlow should not implement PartialOrd or Ord, per RFC 3058:
83 // https://rust-lang.github.io/rfcs/3058-try-trait-v2.html#traits-for-controlflow
84 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
85 pub enum ControlFlow<B, C = ()> {
86 /// Move on to the next phase of the operation as normal.
87 #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
90 /// Exit the operation without running subsequent phases.
91 #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
94 // Yes, the order of the variants doesn't match the type parameters.
95 // They're in this order so that `ControlFlow<A, B>` <-> `Result<B, A>`
96 // is a no-op conversion in the `Try` implementation.
99 #[unstable(feature = "try_trait_v2", issue = "84277")]
100 #[rustc_const_unstable(feature = "const_convert", issue = "88674")]
101 impl<B, C> const ops::Try for ControlFlow<B, C> {
103 type Residual = ControlFlow<B, convert::Infallible>;
106 fn from_output(output: Self::Output) -> Self {
107 ControlFlow::Continue(output)
111 fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
113 ControlFlow::Continue(c) => ControlFlow::Continue(c),
114 ControlFlow::Break(b) => ControlFlow::Break(ControlFlow::Break(b)),
119 #[unstable(feature = "try_trait_v2", issue = "84277")]
120 #[rustc_const_unstable(feature = "const_convert", issue = "88674")]
121 impl<B, C> const ops::FromResidual for ControlFlow<B, C> {
123 fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {
125 ControlFlow::Break(b) => ControlFlow::Break(b),
130 #[unstable(feature = "try_trait_v2_residual", issue = "91285")]
131 #[rustc_const_unstable(feature = "const_try", issue = "74935")]
132 impl<B, C> const ops::Residual<C> for ControlFlow<B, convert::Infallible> {
133 type TryType = ControlFlow<B, C>;
136 impl<B, C> ControlFlow<B, C> {
137 /// Returns `true` if this is a `Break` variant.
142 /// use std::ops::ControlFlow;
144 /// assert!(ControlFlow::<i32, String>::Break(3).is_break());
145 /// assert!(!ControlFlow::<String, i32>::Continue(3).is_break());
148 #[stable(feature = "control_flow_enum_is", since = "1.59.0")]
149 pub fn is_break(&self) -> bool {
150 matches!(*self, ControlFlow::Break(_))
153 /// Returns `true` if this is a `Continue` variant.
158 /// use std::ops::ControlFlow;
160 /// assert!(!ControlFlow::<i32, String>::Break(3).is_continue());
161 /// assert!(ControlFlow::<String, i32>::Continue(3).is_continue());
164 #[stable(feature = "control_flow_enum_is", since = "1.59.0")]
165 pub fn is_continue(&self) -> bool {
166 matches!(*self, ControlFlow::Continue(_))
169 /// Converts the `ControlFlow` into an `Option` which is `Some` if the
170 /// `ControlFlow` was `Break` and `None` otherwise.
175 /// #![feature(control_flow_enum)]
176 /// use std::ops::ControlFlow;
178 /// assert_eq!(ControlFlow::<i32, String>::Break(3).break_value(), Some(3));
179 /// assert_eq!(ControlFlow::<String, i32>::Continue(3).break_value(), None);
182 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
183 pub fn break_value(self) -> Option<B> {
185 ControlFlow::Continue(..) => None,
186 ControlFlow::Break(x) => Some(x),
190 /// Maps `ControlFlow<B, C>` to `ControlFlow<T, C>` by applying a function
191 /// to the break value in case it exists.
193 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
194 pub fn map_break<T, F>(self, f: F) -> ControlFlow<T, C>
199 ControlFlow::Continue(x) => ControlFlow::Continue(x),
200 ControlFlow::Break(x) => ControlFlow::Break(f(x)),
204 /// Converts the `ControlFlow` into an `Option` which is `Some` if the
205 /// `ControlFlow` was `Continue` and `None` otherwise.
210 /// #![feature(control_flow_enum)]
211 /// use std::ops::ControlFlow;
213 /// assert_eq!(ControlFlow::<i32, String>::Break(3).continue_value(), None);
214 /// assert_eq!(ControlFlow::<String, i32>::Continue(3).continue_value(), Some(3));
217 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
218 pub fn continue_value(self) -> Option<C> {
220 ControlFlow::Continue(x) => Some(x),
221 ControlFlow::Break(..) => None,
225 /// Maps `ControlFlow<B, C>` to `ControlFlow<B, T>` by applying a function
226 /// to the continue value in case it exists.
228 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
229 pub fn map_continue<T, F>(self, f: F) -> ControlFlow<B, T>
234 ControlFlow::Continue(x) => ControlFlow::Continue(f(x)),
235 ControlFlow::Break(x) => ControlFlow::Break(x),
240 /// These are used only as part of implementing the iterator adapters.
241 /// They have mediocre names and non-obvious semantics, so aren't
242 /// currently on a path to potential stabilization.
243 impl<R: ops::Try> ControlFlow<R, R::Output> {
244 /// Create a `ControlFlow` from any type implementing `Try`.
246 pub(crate) fn from_try(r: R) -> Self {
248 ControlFlow::Continue(v) => ControlFlow::Continue(v),
249 ControlFlow::Break(v) => ControlFlow::Break(R::from_residual(v)),
253 /// Convert a `ControlFlow` into any type implementing `Try`;
255 pub(crate) fn into_try(self) -> R {
257 ControlFlow::Continue(v) => R::from_output(v),
258 ControlFlow::Break(v) => v,
263 impl<B> ControlFlow<B, ()> {
264 /// It's frequently the case that there's no value needed with `Continue`,
265 /// so this provides a way to avoid typing `(())`, if you prefer it.
270 /// #![feature(control_flow_enum)]
271 /// use std::ops::ControlFlow;
273 /// let mut partial_sum = 0;
274 /// let last_used = (1..10).chain(20..25).try_for_each(|x| {
275 /// partial_sum += x;
276 /// if partial_sum > 100 { ControlFlow::Break(x) }
277 /// else { ControlFlow::CONTINUE }
279 /// assert_eq!(last_used.break_value(), Some(22));
281 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
282 pub const CONTINUE: Self = ControlFlow::Continue(());
285 impl<C> ControlFlow<(), C> {
286 /// APIs like `try_for_each` don't need values with `Break`,
287 /// so this provides a way to avoid typing `(())`, if you prefer it.
292 /// #![feature(control_flow_enum)]
293 /// use std::ops::ControlFlow;
295 /// let mut partial_sum = 0;
296 /// (1..10).chain(20..25).try_for_each(|x| {
297 /// if partial_sum > 100 { ControlFlow::BREAK }
298 /// else { partial_sum += x; ControlFlow::CONTINUE }
300 /// assert_eq!(partial_sum, 108);
302 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
303 pub const BREAK: Self = ControlFlow::Break(());