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.
12 /// Early-exiting from [`Iterator::try_for_each`]:
14 /// #![feature(control_flow_enum)]
15 /// use std::ops::ControlFlow;
17 /// let r = (2..100).try_for_each(|x| {
19 /// return ControlFlow::Break(x)
22 /// ControlFlow::Continue(())
24 /// assert_eq!(r, ControlFlow::Break(13));
27 /// A basic tree traversal:
29 /// #![feature(control_flow_enum)]
30 /// use std::ops::ControlFlow;
32 /// pub struct TreeNode<T> {
34 /// left: Option<Box<TreeNode<T>>>,
35 /// right: Option<Box<TreeNode<T>>>,
38 /// impl<T> TreeNode<T> {
39 /// pub fn traverse_inorder<B>(&self, mut f: impl FnMut(&T) -> ControlFlow<B>) -> ControlFlow<B> {
40 /// if let Some(left) = &self.left {
41 /// left.traverse_inorder(&mut f)?;
44 /// if let Some(right) = &self.right {
45 /// right.traverse_inorder(&mut f)?;
47 /// ControlFlow::Continue(())
51 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
52 #[derive(Debug, Clone, Copy, PartialEq)]
53 pub enum ControlFlow<B, C = ()> {
54 /// Move on to the next phase of the operation as normal.
55 #[cfg_attr(not(bootstrap), lang = "Continue")]
57 /// Exit the operation without running subsequent phases.
58 #[cfg_attr(not(bootstrap), lang = "Break")]
60 // Yes, the order of the variants doesn't match the type parameters.
61 // They're in this order so that `ControlFlow<A, B>` <-> `Result<B, A>`
62 // is a no-op conversion in the `Try` implementation.
65 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
66 impl<B, C> ops::TryV1 for ControlFlow<B, C> {
70 fn into_result(self) -> Result<Self::Output, Self::Error> {
72 ControlFlow::Continue(y) => Ok(y),
73 ControlFlow::Break(x) => Err(x),
77 fn from_error(v: Self::Error) -> Self {
81 fn from_ok(v: Self::Output) -> Self {
82 ControlFlow::Continue(v)
86 #[unstable(feature = "try_trait_v2", issue = "84277")]
87 impl<B, C> ops::TryV2 for ControlFlow<B, C> {
89 type Residual = ControlFlow<B, convert::Infallible>;
92 fn from_output(output: Self::Output) -> Self {
93 ControlFlow::Continue(output)
97 fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
99 ControlFlow::Continue(c) => ControlFlow::Continue(c),
100 ControlFlow::Break(b) => ControlFlow::Break(ControlFlow::Break(b)),
105 #[unstable(feature = "try_trait_v2", issue = "84277")]
106 impl<B, C> ops::FromResidual for ControlFlow<B, C> {
108 fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {
110 ControlFlow::Break(b) => ControlFlow::Break(b),
115 impl<B, C> ControlFlow<B, C> {
116 /// Returns `true` if this is a `Break` variant.
121 /// #![feature(control_flow_enum)]
122 /// use std::ops::ControlFlow;
124 /// assert!(ControlFlow::<i32, String>::Break(3).is_break());
125 /// assert!(!ControlFlow::<String, i32>::Continue(3).is_break());
128 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
129 pub fn is_break(&self) -> bool {
130 matches!(*self, ControlFlow::Break(_))
133 /// Returns `true` if this is a `Continue` variant.
138 /// #![feature(control_flow_enum)]
139 /// use std::ops::ControlFlow;
141 /// assert!(!ControlFlow::<i32, String>::Break(3).is_continue());
142 /// assert!(ControlFlow::<String, i32>::Continue(3).is_continue());
145 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
146 pub fn is_continue(&self) -> bool {
147 matches!(*self, ControlFlow::Continue(_))
150 /// Converts the `ControlFlow` into an `Option` which is `Some` if the
151 /// `ControlFlow` was `Break` and `None` otherwise.
156 /// #![feature(control_flow_enum)]
157 /// use std::ops::ControlFlow;
159 /// assert_eq!(ControlFlow::<i32, String>::Break(3).break_value(), Some(3));
160 /// assert_eq!(ControlFlow::<String, i32>::Continue(3).break_value(), None);
163 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
164 pub fn break_value(self) -> Option<B> {
166 ControlFlow::Continue(..) => None,
167 ControlFlow::Break(x) => Some(x),
171 /// Maps `ControlFlow<B, C>` to `ControlFlow<T, C>` by applying a function
172 /// to the break value in case it exists.
174 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
175 pub fn map_break<T, F>(self, f: F) -> ControlFlow<T, C>
180 ControlFlow::Continue(x) => ControlFlow::Continue(x),
181 ControlFlow::Break(x) => ControlFlow::Break(f(x)),
187 impl<R: ops::TryV1> ControlFlow<R, R::Output> {
188 /// Create a `ControlFlow` from any type implementing `Try`.
189 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
191 pub fn from_try(r: R) -> Self {
192 match R::into_result(r) {
193 Ok(v) => ControlFlow::Continue(v),
194 Err(v) => ControlFlow::Break(R::from_error(v)),
198 /// Convert a `ControlFlow` into any type implementing `Try`;
199 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
201 pub fn into_try(self) -> R {
203 ControlFlow::Continue(v) => R::from_ok(v),
204 ControlFlow::Break(v) => v,
209 #[cfg(not(bootstrap))]
210 impl<R: ops::TryV2> ControlFlow<R, R::Output> {
211 /// Create a `ControlFlow` from any type implementing `Try`.
212 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
214 pub fn from_try(r: R) -> Self {
216 ControlFlow::Continue(v) => ControlFlow::Continue(v),
217 ControlFlow::Break(v) => ControlFlow::Break(R::from_residual(v)),
221 /// Convert a `ControlFlow` into any type implementing `Try`;
222 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
224 pub fn into_try(self) -> R {
226 ControlFlow::Continue(v) => R::from_output(v),
227 ControlFlow::Break(v) => v,
232 impl<B> ControlFlow<B, ()> {
233 /// It's frequently the case that there's no value needed with `Continue`,
234 /// so this provides a way to avoid typing `(())`, if you prefer it.
239 /// #![feature(control_flow_enum)]
240 /// use std::ops::ControlFlow;
242 /// let mut partial_sum = 0;
243 /// let last_used = (1..10).chain(20..25).try_for_each(|x| {
244 /// partial_sum += x;
245 /// if partial_sum > 100 { ControlFlow::Break(x) }
246 /// else { ControlFlow::CONTINUE }
248 /// assert_eq!(last_used.break_value(), Some(22));
250 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
251 pub const CONTINUE: Self = ControlFlow::Continue(());
254 impl<C> ControlFlow<(), C> {
255 /// APIs like `try_for_each` don't need values with `Break`,
256 /// so this provides a way to avoid typing `(())`, if you prefer it.
261 /// #![feature(control_flow_enum)]
262 /// use std::ops::ControlFlow;
264 /// let mut partial_sum = 0;
265 /// (1..10).chain(20..25).try_for_each(|x| {
266 /// if partial_sum > 100 { ControlFlow::BREAK }
267 /// else { partial_sum += x; ControlFlow::CONTINUE }
269 /// assert_eq!(partial_sum, 108);
271 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
272 pub const BREAK: Self = ControlFlow::Break(());