2 use crate::ops::{self, Try};
4 /// Used to tell an operation whether it should exit early or go on as usual.
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.
13 /// Early-exiting from [`Iterator::try_for_each`]:
15 /// #![feature(control_flow_enum)]
16 /// use std::ops::ControlFlow;
18 /// let r = (2..100).try_for_each(|x| {
20 /// return ControlFlow::Break(x)
23 /// ControlFlow::Continue(())
25 /// assert_eq!(r, ControlFlow::Break(13));
28 /// A basic tree traversal:
30 /// #![feature(control_flow_enum)]
31 /// use std::ops::ControlFlow;
33 /// pub struct TreeNode<T> {
35 /// left: Option<Box<TreeNode<T>>>,
36 /// right: Option<Box<TreeNode<T>>>,
39 /// impl<T> TreeNode<T> {
40 /// pub fn traverse_inorder<B>(&self, mut f: impl FnMut(&T) -> ControlFlow<B>) -> ControlFlow<B> {
41 /// if let Some(left) = &self.left {
42 /// left.traverse_inorder(&mut f)?;
45 /// if let Some(right) = &self.right {
46 /// right.traverse_inorder(&mut f)?;
48 /// ControlFlow::Continue(())
52 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
53 #[derive(Debug, Clone, Copy, PartialEq)]
54 pub enum ControlFlow<B, C = ()> {
55 /// Move on to the next phase of the operation as normal.
57 /// Exit the operation without running subsequent phases.
59 // Yes, the order of the variants doesn't match the type parameters.
60 // They're in this order so that `ControlFlow<A, B>` <-> `Result<B, A>`
61 // is a no-op conversion in the `Try` implementation.
64 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
65 impl<B, C> Try for ControlFlow<B, C> {
69 fn into_result(self) -> Result<Self::Ok, Self::Error> {
71 ControlFlow::Continue(y) => Ok(y),
72 ControlFlow::Break(x) => Err(x),
76 fn from_error(v: Self::Error) -> Self {
80 fn from_ok(v: Self::Ok) -> Self {
81 ControlFlow::Continue(v)
85 #[unstable(feature = "try_trait_v2", issue = "84277")]
86 impl<B, C> ops::TryV2 for ControlFlow<B, C> {
88 type Residual = ControlFlow<B, convert::Infallible>;
91 fn from_output(output: Self::Output) -> Self {
92 ControlFlow::Continue(output)
96 fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
98 ControlFlow::Continue(c) => ControlFlow::Continue(c),
99 ControlFlow::Break(b) => ControlFlow::Break(ControlFlow::Break(b)),
104 #[unstable(feature = "try_trait_v2", issue = "84277")]
105 impl<B, C> ops::FromResidual for ControlFlow<B, C> {
107 fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {
109 ControlFlow::Break(b) => ControlFlow::Break(b),
114 impl<B, C> ControlFlow<B, C> {
115 /// Returns `true` if this is a `Break` variant.
120 /// #![feature(control_flow_enum)]
121 /// use std::ops::ControlFlow;
123 /// assert!(ControlFlow::<i32, String>::Break(3).is_break());
124 /// assert!(!ControlFlow::<String, i32>::Continue(3).is_break());
127 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
128 pub fn is_break(&self) -> bool {
129 matches!(*self, ControlFlow::Break(_))
132 /// Returns `true` if this is a `Continue` variant.
137 /// #![feature(control_flow_enum)]
138 /// use std::ops::ControlFlow;
140 /// assert!(!ControlFlow::<i32, String>::Break(3).is_continue());
141 /// assert!(ControlFlow::<String, i32>::Continue(3).is_continue());
144 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
145 pub fn is_continue(&self) -> bool {
146 matches!(*self, ControlFlow::Continue(_))
149 /// Converts the `ControlFlow` into an `Option` which is `Some` if the
150 /// `ControlFlow` was `Break` and `None` otherwise.
155 /// #![feature(control_flow_enum)]
156 /// use std::ops::ControlFlow;
158 /// assert_eq!(ControlFlow::<i32, String>::Break(3).break_value(), Some(3));
159 /// assert_eq!(ControlFlow::<String, i32>::Continue(3).break_value(), None);
162 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
163 pub fn break_value(self) -> Option<B> {
165 ControlFlow::Continue(..) => None,
166 ControlFlow::Break(x) => Some(x),
170 /// Maps `ControlFlow<B, C>` to `ControlFlow<T, C>` by applying a function
171 /// to the break value in case it exists.
173 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
174 pub fn map_break<T, F>(self, f: F) -> ControlFlow<T, C>
179 ControlFlow::Continue(x) => ControlFlow::Continue(x),
180 ControlFlow::Break(x) => ControlFlow::Break(f(x)),
185 impl<R: Try> ControlFlow<R, R::Ok> {
186 /// Create a `ControlFlow` from any type implementing `Try`.
187 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
189 pub fn from_try(r: R) -> Self {
190 match Try::into_result(r) {
191 Ok(v) => ControlFlow::Continue(v),
192 Err(v) => ControlFlow::Break(Try::from_error(v)),
196 /// Convert a `ControlFlow` into any type implementing `Try`;
197 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
199 pub fn into_try(self) -> R {
201 ControlFlow::Continue(v) => Try::from_ok(v),
202 ControlFlow::Break(v) => v,
207 impl<B> ControlFlow<B, ()> {
208 /// It's frequently the case that there's no value needed with `Continue`,
209 /// so this provides a way to avoid typing `(())`, if you prefer it.
214 /// #![feature(control_flow_enum)]
215 /// use std::ops::ControlFlow;
217 /// let mut partial_sum = 0;
218 /// let last_used = (1..10).chain(20..25).try_for_each(|x| {
219 /// partial_sum += x;
220 /// if partial_sum > 100 { ControlFlow::Break(x) }
221 /// else { ControlFlow::CONTINUE }
223 /// assert_eq!(last_used.break_value(), Some(22));
225 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
226 pub const CONTINUE: Self = ControlFlow::Continue(());
229 impl<C> ControlFlow<(), C> {
230 /// APIs like `try_for_each` don't need values with `Break`,
231 /// so this provides a way to avoid typing `(())`, if you prefer it.
236 /// #![feature(control_flow_enum)]
237 /// use std::ops::ControlFlow;
239 /// let mut partial_sum = 0;
240 /// (1..10).chain(20..25).try_for_each(|x| {
241 /// if partial_sum > 100 { ControlFlow::BREAK }
242 /// else { partial_sum += x; ControlFlow::CONTINUE }
244 /// assert_eq!(partial_sum, 108);
246 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
247 pub const BREAK: Self = ControlFlow::Break(());