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#[cfg_attr(not(test), rustc_diagnostic_item = "ControlFlow")]
83// ControlFlow should not implement PartialOrd or Ord, per RFC 3058:
84// https://rust-lang.github.io/rfcs/3058-try-trait-v2.html#traits-for-controlflow
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
86pub enum ControlFlow<B, C = ()> {
87 /// Move on to the next phase of the operation as normal.
88 #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
89 #[lang = "Continue"]
90 Continue(C),
91 /// Exit the operation without running subsequent phases.
92 #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
93 #[lang = "Break"]
94 Break(B),
95 // Yes, the order of the variants doesn't match the type parameters.
96 // They're in this order so that `ControlFlow<A, B>` <-> `Result<B, A>`
97 // is a no-op conversion in the `Try` implementation.
98}
99
100#[unstable(feature = "try_trait_v2", issue = "84277")]
101impl<B, C> ops::Try for ControlFlow<B, C> {
102 type Output = C;
103 type Residual = ControlFlow<B, convert::Infallible>;
104
105 #[inline]
106 fn from_output(output: Self::Output) -> Self {
107 ControlFlow::Continue(output)
108 }
109
110 #[inline]
111 fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
112 match self {
113 ControlFlow::Continue(c) => ControlFlow::Continue(c),
114 ControlFlow::Break(b) => ControlFlow::Break(ControlFlow::Break(b)),
115 }
116 }
117}
118
119#[unstable(feature = "try_trait_v2", issue = "84277")]
120// Note: manually specifying the residual type instead of using the default to work around
121// https://github.com/rust-lang/rust/issues/99940
122impl<B, C> ops::FromResidual<ControlFlow<B, convert::Infallible>> for ControlFlow<B, C> {
123 #[inline]
124 fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {
125 match residual {
126 ControlFlow::Break(b) => ControlFlow::Break(b),
127 }
128 }
129}
130
131#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
132impl<B, C> ops::Residual<C> for ControlFlow<B, convert::Infallible> {
133 type TryType = ControlFlow<B, C>;
134}
135
136impl<B, C> ControlFlow<B, C> {
137 /// Returns `true` if this is a `Break` variant.
138 ///
139 /// # Examples
140 ///
141 /// ```
142 /// use std::ops::ControlFlow;
143 ///
144 /// assert!(ControlFlow::<&str, i32>::Break("Stop right there!").is_break());
145 /// assert!(!ControlFlow::<&str, i32>::Continue(3).is_break());
146 /// ```
147 #[inline]
148 #[stable(feature = "control_flow_enum_is", since = "1.59.0")]
149 pub fn is_break(&self) -> bool {
150 matches!(*self, ControlFlow::Break(_))
151 }
152
153 /// Returns `true` if this is a `Continue` variant.
154 ///
155 /// # Examples
156 ///
157 /// ```
158 /// use std::ops::ControlFlow;
159 ///
160 /// assert!(!ControlFlow::<&str, i32>::Break("Stop right there!").is_continue());
161 /// assert!(ControlFlow::<&str, i32>::Continue(3).is_continue());
162 /// ```
163 #[inline]
164 #[stable(feature = "control_flow_enum_is", since = "1.59.0")]
165 pub fn is_continue(&self) -> bool {
166 matches!(*self, ControlFlow::Continue(_))
167 }
168
169 /// Converts the `ControlFlow` into an `Option` which is `Some` if the
170 /// `ControlFlow` was `Break` and `None` otherwise.
171 ///
172 /// # Examples
173 ///
174 /// ```
175 /// use std::ops::ControlFlow;
176 ///
177 /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").break_value(), Some("Stop right there!"));
178 /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).break_value(), None);
179 /// ```
180 #[inline]
181 #[stable(feature = "control_flow_enum", since = "1.83.0")]
182 pub fn break_value(self) -> Option<B> {
183 match self {
184 ControlFlow::Continue(..) => None,
185 ControlFlow::Break(x) => Some(x),
186 }
187 }
188
189 /// Maps `ControlFlow<B, C>` to `ControlFlow<T, C>` by applying a function
190 /// to the break value in case it exists.
191 #[inline]
192 #[stable(feature = "control_flow_enum", since = "1.83.0")]
193 pub fn map_break<T>(self, f: impl FnOnce(B) -> T) -> ControlFlow<T, C> {
194 match self {
195 ControlFlow::Continue(x) => ControlFlow::Continue(x),
196 ControlFlow::Break(x) => ControlFlow::Break(f(x)),
197 }
198 }
199
200 /// Converts the `ControlFlow` into an `Option` which is `Some` if the
201 /// `ControlFlow` was `Continue` and `None` otherwise.
202 ///
203 /// # Examples
204 ///
205 /// ```
206 /// use std::ops::ControlFlow;
207 ///
208 /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").continue_value(), None);
209 /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).continue_value(), Some(3));
210 /// ```
211 #[inline]
212 #[stable(feature = "control_flow_enum", since = "1.83.0")]
213 pub fn continue_value(self) -> Option<C> {
214 match self {
215 ControlFlow::Continue(x) => Some(x),
216 ControlFlow::Break(..) => None,
217 }
218 }
219
220 /// Maps `ControlFlow<B, C>` to `ControlFlow<B, T>` by applying a function
221 /// to the continue value in case it exists.
222 #[inline]
223 #[stable(feature = "control_flow_enum", since = "1.83.0")]
224 pub fn map_continue<T>(self, f: impl FnOnce(C) -> T) -> ControlFlow<B, T> {
225 match self {
226 ControlFlow::Continue(x) => ControlFlow::Continue(f(x)),
227 ControlFlow::Break(x) => ControlFlow::Break(x),
228 }
229 }
230}
231
232/// These are used only as part of implementing the iterator adapters.
233/// They have mediocre names and non-obvious semantics, so aren't
234/// currently on a path to potential stabilization.
235impl<R: ops::Try> ControlFlow<R, R::Output> {
236 /// Creates a `ControlFlow` from any type implementing `Try`.
237 #[inline]
238 pub(crate) fn from_try(r: R) -> Self {
239 match R::branch(r) {
240 ControlFlow::Continue(v) => ControlFlow::Continue(v),
241 ControlFlow::Break(v) => ControlFlow::Break(R::from_residual(v)),
242 }
243 }
244
245 /// Converts a `ControlFlow` into any type implementing `Try`.
246 #[inline]
247 pub(crate) fn into_try(self) -> R {
248 match self {
249 ControlFlow::Continue(v) => R::from_output(v),
250 ControlFlow::Break(v) => v,
251 }
252 }
253}