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// 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)]
85pub 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")]
88 #[lang = "Continue"]
89 Continue(C),
90 /// Exit the operation without running subsequent phases.
91 #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
92 #[lang = "Break"]
93 Break(B),
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.
97}
98
99#[unstable(feature = "try_trait_v2", issue = "84277")]
100impl<B, C> ops::Try for ControlFlow<B, C> {
101 type Output = C;
102 type Residual = ControlFlow<B, convert::Infallible>;
103
104 #[inline]
105 fn from_output(output: Self::Output) -> Self {
106 ControlFlow::Continue(output)
107 }
108
109 #[inline]
110 fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
111 match self {
112 ControlFlow::Continue(c: C) => ControlFlow::Continue(c),
113 ControlFlow::Break(b: B) => ControlFlow::Break(ControlFlow::Break(b)),
114 }
115 }
116}
117
118#[unstable(feature = "try_trait_v2", issue = "84277")]
119impl<B, C> ops::FromResidual for ControlFlow<B, C> {
120 #[inline]
121 fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {
122 match residual {
123 ControlFlow::Break(b: B) => ControlFlow::Break(b),
124 }
125 }
126}
127
128#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
129impl<B, C> ops::Residual<C> for ControlFlow<B, convert::Infallible> {
130 type TryType = ControlFlow<B, C>;
131}
132
133impl<B, C> ControlFlow<B, C> {
134 /// Returns `true` if this is a `Break` variant.
135 ///
136 /// # Examples
137 ///
138 /// ```
139 /// use std::ops::ControlFlow;
140 ///
141 /// assert!(ControlFlow::<i32, String>::Break(3).is_break());
142 /// assert!(!ControlFlow::<String, i32>::Continue(3).is_break());
143 /// ```
144 #[inline]
145 #[stable(feature = "control_flow_enum_is", since = "1.59.0")]
146 pub fn is_break(&self) -> bool {
147 matches!(*self, ControlFlow::Break(_))
148 }
149
150 /// Returns `true` if this is a `Continue` variant.
151 ///
152 /// # Examples
153 ///
154 /// ```
155 /// use std::ops::ControlFlow;
156 ///
157 /// assert!(!ControlFlow::<i32, String>::Break(3).is_continue());
158 /// assert!(ControlFlow::<String, i32>::Continue(3).is_continue());
159 /// ```
160 #[inline]
161 #[stable(feature = "control_flow_enum_is", since = "1.59.0")]
162 pub fn is_continue(&self) -> bool {
163 matches!(*self, ControlFlow::Continue(_))
164 }
165
166 /// Converts the `ControlFlow` into an `Option` which is `Some` if the
167 /// `ControlFlow` was `Break` and `None` otherwise.
168 ///
169 /// # Examples
170 ///
171 /// ```
172 /// #![feature(control_flow_enum)]
173 /// use std::ops::ControlFlow;
174 ///
175 /// assert_eq!(ControlFlow::<i32, String>::Break(3).break_value(), Some(3));
176 /// assert_eq!(ControlFlow::<String, i32>::Continue(3).break_value(), None);
177 /// ```
178 #[inline]
179 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
180 pub fn break_value(self) -> Option<B> {
181 match self {
182 ControlFlow::Continue(..) => None,
183 ControlFlow::Break(x) => Some(x),
184 }
185 }
186
187 /// Maps `ControlFlow<B, C>` to `ControlFlow<T, C>` by applying a function
188 /// to the break value in case it exists.
189 #[inline]
190 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
191 pub fn map_break<T, F>(self, f: F) -> ControlFlow<T, C>
192 where
193 F: FnOnce(B) -> T,
194 {
195 match self {
196 ControlFlow::Continue(x) => ControlFlow::Continue(x),
197 ControlFlow::Break(x) => ControlFlow::Break(f(x)),
198 }
199 }
200
201 /// Converts the `ControlFlow` into an `Option` which is `Some` if the
202 /// `ControlFlow` was `Continue` and `None` otherwise.
203 ///
204 /// # Examples
205 ///
206 /// ```
207 /// #![feature(control_flow_enum)]
208 /// use std::ops::ControlFlow;
209 ///
210 /// assert_eq!(ControlFlow::<i32, String>::Break(3).continue_value(), None);
211 /// assert_eq!(ControlFlow::<String, i32>::Continue(3).continue_value(), Some(3));
212 /// ```
213 #[inline]
214 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
215 pub fn continue_value(self) -> Option<C> {
216 match self {
217 ControlFlow::Continue(x) => Some(x),
218 ControlFlow::Break(..) => None,
219 }
220 }
221
222 /// Maps `ControlFlow<B, C>` to `ControlFlow<B, T>` by applying a function
223 /// to the continue value in case it exists.
224 #[inline]
225 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
226 pub fn map_continue<T, F>(self, f: F) -> ControlFlow<B, T>
227 where
228 F: FnOnce(C) -> T,
229 {
230 match self {
231 ControlFlow::Continue(x) => ControlFlow::Continue(f(x)),
232 ControlFlow::Break(x) => ControlFlow::Break(x),
233 }
234 }
235}
236
237/// These are used only as part of implementing the iterator adapters.
238/// They have mediocre names and non-obvious semantics, so aren't
239/// currently on a path to potential stabilization.
240impl<R: ops::Try> ControlFlow<R, R::Output> {
241 /// Create a `ControlFlow` from any type implementing `Try`.
242 #[inline]
243 pub(crate) fn from_try(r: R) -> Self {
244 match R::branch(self:r) {
245 ControlFlow::Continue(v: ::Output) => ControlFlow::Continue(v),
246 ControlFlow::Break(v: ::Residual) => ControlFlow::Break(R::from_residual(v)),
247 }
248 }
249
250 /// Convert a `ControlFlow` into any type implementing `Try`;
251 #[inline]
252 pub(crate) fn into_try(self) -> R {
253 match self {
254 ControlFlow::Continue(v: ::Output) => R::from_output(v),
255 ControlFlow::Break(v: R) => v,
256 }
257 }
258}
259