]> git.lizzy.rs Git - rust.git/blob - library/core/src/ops/control_flow.rs
Auto merge of #85830 - bjorn3:separate_provide_extern, r=cjgillot
[rust.git] / library / core / src / ops / control_flow.rs
1 use 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 /// # Examples
11 ///
12 /// Early-exiting from [`Iterator::try_for_each`]:
13 /// ```
14 /// use std::ops::ControlFlow;
15 ///
16 /// let r = (2..100).try_for_each(|x| {
17 ///     if 403 % x == 0 {
18 ///         return ControlFlow::Break(x)
19 ///     }
20 ///
21 ///     ControlFlow::Continue(())
22 /// });
23 /// assert_eq!(r, ControlFlow::Break(13));
24 /// ```
25 ///
26 /// A basic tree traversal:
27 /// ```
28 /// use std::ops::ControlFlow;
29 ///
30 /// pub struct TreeNode<T> {
31 ///     value: T,
32 ///     left: Option<Box<TreeNode<T>>>,
33 ///     right: Option<Box<TreeNode<T>>>,
34 /// }
35 ///
36 /// impl<T> TreeNode<T> {
37 ///     pub fn traverse_inorder<B>(&self, f: &mut impl FnMut(&T) -> ControlFlow<B>) -> ControlFlow<B> {
38 ///         if let Some(left) = &self.left {
39 ///             left.traverse_inorder(f)?;
40 ///         }
41 ///         f(&self.value)?;
42 ///         if let Some(right) = &self.right {
43 ///             right.traverse_inorder(f)?;
44 ///         }
45 ///         ControlFlow::Continue(())
46 ///     }
47 ///     fn leaf(value: T) -> Option<Box<TreeNode<T>>> {
48 ///         Some(Box::new(Self { value, left: None, right: None }))
49 ///     }
50 /// }
51 ///
52 /// let node = TreeNode {
53 ///     value: 0,
54 ///     left: TreeNode::leaf(1),
55 ///     right: Some(Box::new(TreeNode {
56 ///         value: -1,
57 ///         left: TreeNode::leaf(5),
58 ///         right: TreeNode::leaf(2),
59 ///     }))
60 /// };
61 /// let mut sum = 0;
62 ///
63 /// let res = node.traverse_inorder(&mut |val| {
64 ///     if *val < 0 {
65 ///         ControlFlow::Break(*val)
66 ///     } else {
67 ///         sum += *val;
68 ///         ControlFlow::Continue(())
69 ///     }
70 /// });
71 /// assert_eq!(res, ControlFlow::Break(-1));
72 /// assert_eq!(sum, 6);
73 /// ```
74 #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
75 #[derive(Debug, Clone, Copy, PartialEq)]
76 pub enum ControlFlow<B, C = ()> {
77     /// Move on to the next phase of the operation as normal.
78     #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
79     #[lang = "Continue"]
80     Continue(C),
81     /// Exit the operation without running subsequent phases.
82     #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
83     #[lang = "Break"]
84     Break(B),
85     // Yes, the order of the variants doesn't match the type parameters.
86     // They're in this order so that `ControlFlow<A, B>` <-> `Result<B, A>`
87     // is a no-op conversion in the `Try` implementation.
88 }
89
90 #[unstable(feature = "try_trait_v2", issue = "84277")]
91 impl<B, C> ops::Try for ControlFlow<B, C> {
92     type Output = C;
93     type Residual = ControlFlow<B, convert::Infallible>;
94
95     #[inline]
96     fn from_output(output: Self::Output) -> Self {
97         ControlFlow::Continue(output)
98     }
99
100     #[inline]
101     fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
102         match self {
103             ControlFlow::Continue(c) => ControlFlow::Continue(c),
104             ControlFlow::Break(b) => ControlFlow::Break(ControlFlow::Break(b)),
105         }
106     }
107 }
108
109 #[unstable(feature = "try_trait_v2", issue = "84277")]
110 impl<B, C> ops::FromResidual for ControlFlow<B, C> {
111     #[inline]
112     fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {
113         match residual {
114             ControlFlow::Break(b) => ControlFlow::Break(b),
115         }
116     }
117 }
118
119 impl<B, C> ControlFlow<B, C> {
120     /// Returns `true` if this is a `Break` variant.
121     ///
122     /// # Examples
123     ///
124     /// ```
125     /// #![feature(control_flow_enum)]
126     /// use std::ops::ControlFlow;
127     ///
128     /// assert!(ControlFlow::<i32, String>::Break(3).is_break());
129     /// assert!(!ControlFlow::<String, i32>::Continue(3).is_break());
130     /// ```
131     #[inline]
132     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
133     pub fn is_break(&self) -> bool {
134         matches!(*self, ControlFlow::Break(_))
135     }
136
137     /// Returns `true` if this is a `Continue` variant.
138     ///
139     /// # Examples
140     ///
141     /// ```
142     /// #![feature(control_flow_enum)]
143     /// use std::ops::ControlFlow;
144     ///
145     /// assert!(!ControlFlow::<i32, String>::Break(3).is_continue());
146     /// assert!(ControlFlow::<String, i32>::Continue(3).is_continue());
147     /// ```
148     #[inline]
149     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
150     pub fn is_continue(&self) -> bool {
151         matches!(*self, ControlFlow::Continue(_))
152     }
153
154     /// Converts the `ControlFlow` into an `Option` which is `Some` if the
155     /// `ControlFlow` was `Break` and `None` otherwise.
156     ///
157     /// # Examples
158     ///
159     /// ```
160     /// #![feature(control_flow_enum)]
161     /// use std::ops::ControlFlow;
162     ///
163     /// assert_eq!(ControlFlow::<i32, String>::Break(3).break_value(), Some(3));
164     /// assert_eq!(ControlFlow::<String, i32>::Continue(3).break_value(), None);
165     /// ```
166     #[inline]
167     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
168     pub fn break_value(self) -> Option<B> {
169         match self {
170             ControlFlow::Continue(..) => None,
171             ControlFlow::Break(x) => Some(x),
172         }
173     }
174
175     /// Maps `ControlFlow<B, C>` to `ControlFlow<T, C>` by applying a function
176     /// to the break value in case it exists.
177     #[inline]
178     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
179     pub fn map_break<T, F>(self, f: F) -> ControlFlow<T, C>
180     where
181         F: FnOnce(B) -> T,
182     {
183         match self {
184             ControlFlow::Continue(x) => ControlFlow::Continue(x),
185             ControlFlow::Break(x) => ControlFlow::Break(f(x)),
186         }
187     }
188 }
189
190 /// These are used only as part of implementing the iterator adapters.
191 /// They have mediocre names and non-obvious semantics, so aren't
192 /// currently on a path to potential stabilization.
193 impl<R: ops::Try> ControlFlow<R, R::Output> {
194     /// Create a `ControlFlow` from any type implementing `Try`.
195     #[inline]
196     pub(crate) fn from_try(r: R) -> Self {
197         match R::branch(r) {
198             ControlFlow::Continue(v) => ControlFlow::Continue(v),
199             ControlFlow::Break(v) => ControlFlow::Break(R::from_residual(v)),
200         }
201     }
202
203     /// Convert a `ControlFlow` into any type implementing `Try`;
204     #[inline]
205     pub(crate) fn into_try(self) -> R {
206         match self {
207             ControlFlow::Continue(v) => R::from_output(v),
208             ControlFlow::Break(v) => v,
209         }
210     }
211 }
212
213 impl<B> ControlFlow<B, ()> {
214     /// It's frequently the case that there's no value needed with `Continue`,
215     /// so this provides a way to avoid typing `(())`, if you prefer it.
216     ///
217     /// # Examples
218     ///
219     /// ```
220     /// #![feature(control_flow_enum)]
221     /// use std::ops::ControlFlow;
222     ///
223     /// let mut partial_sum = 0;
224     /// let last_used = (1..10).chain(20..25).try_for_each(|x| {
225     ///     partial_sum += x;
226     ///     if partial_sum > 100 { ControlFlow::Break(x) }
227     ///     else { ControlFlow::CONTINUE }
228     /// });
229     /// assert_eq!(last_used.break_value(), Some(22));
230     /// ```
231     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
232     pub const CONTINUE: Self = ControlFlow::Continue(());
233 }
234
235 impl<C> ControlFlow<(), C> {
236     /// APIs like `try_for_each` don't need values with `Break`,
237     /// so this provides a way to avoid typing `(())`, if you prefer it.
238     ///
239     /// # Examples
240     ///
241     /// ```
242     /// #![feature(control_flow_enum)]
243     /// use std::ops::ControlFlow;
244     ///
245     /// let mut partial_sum = 0;
246     /// (1..10).chain(20..25).try_for_each(|x| {
247     ///     if partial_sum > 100 { ControlFlow::BREAK }
248     ///     else { partial_sum += x; ControlFlow::CONTINUE }
249     /// });
250     /// assert_eq!(partial_sum, 108);
251     /// ```
252     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
253     pub const BREAK: Self = ControlFlow::Break(());
254 }