]> git.lizzy.rs Git - rust.git/blob - library/core/src/ops/control_flow.rs
Rollup merge of #80874 - jyn514:intra-doc-docs, r=Manishearth
[rust.git] / library / core / src / ops / control_flow.rs
1 use crate::ops::Try;
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 /// #![feature(control_flow_enum)]
15 /// use std::ops::ControlFlow;
16 ///
17 /// let r = (2..100).try_for_each(|x| {
18 ///     if 403 % x == 0 {
19 ///         return ControlFlow::Break(x)
20 ///     }
21 ///
22 ///     ControlFlow::Continue(())
23 /// });
24 /// assert_eq!(r, ControlFlow::Break(13));
25 /// ```
26 ///
27 /// A basic tree traversal:
28 /// ```no_run
29 /// #![feature(control_flow_enum)]
30 /// use std::ops::ControlFlow;
31 ///
32 /// pub struct TreeNode<T> {
33 ///     value: T,
34 ///     left: Option<Box<TreeNode<T>>>,
35 ///     right: Option<Box<TreeNode<T>>>,
36 /// }
37 ///
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)?;
42 ///         }
43 ///         f(&self.value)?;
44 ///         if let Some(right) = &self.right {
45 ///             right.traverse_inorder(&mut f)?;
46 ///         }
47 ///         ControlFlow::Continue(())
48 ///     }
49 /// }
50 /// ```
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     Continue(C),
56     /// Exit the operation without running subsequent phases.
57     Break(B),
58     // Yes, the order of the variants doesn't match the type parameters.
59     // They're in this order so that `ControlFlow<A, B>` <-> `Result<B, A>`
60     // is a no-op conversion in the `Try` implementation.
61 }
62
63 #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
64 impl<B, C> Try for ControlFlow<B, C> {
65     type Ok = C;
66     type Error = B;
67     #[inline]
68     fn into_result(self) -> Result<Self::Ok, Self::Error> {
69         match self {
70             ControlFlow::Continue(y) => Ok(y),
71             ControlFlow::Break(x) => Err(x),
72         }
73     }
74     #[inline]
75     fn from_error(v: Self::Error) -> Self {
76         ControlFlow::Break(v)
77     }
78     #[inline]
79     fn from_ok(v: Self::Ok) -> Self {
80         ControlFlow::Continue(v)
81     }
82 }
83
84 impl<B, C> ControlFlow<B, C> {
85     /// Returns `true` if this is a `Break` variant.
86     ///
87     /// # Examples
88     ///
89     /// ```
90     /// #![feature(control_flow_enum)]
91     /// use std::ops::ControlFlow;
92     ///
93     /// assert!(ControlFlow::<i32, String>::Break(3).is_break());
94     /// assert!(!ControlFlow::<String, i32>::Continue(3).is_break());
95     /// ```
96     #[inline]
97     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
98     pub fn is_break(&self) -> bool {
99         matches!(*self, ControlFlow::Break(_))
100     }
101
102     /// Returns `true` if this is a `Continue` variant.
103     ///
104     /// # Examples
105     ///
106     /// ```
107     /// #![feature(control_flow_enum)]
108     /// use std::ops::ControlFlow;
109     ///
110     /// assert!(!ControlFlow::<i32, String>::Break(3).is_continue());
111     /// assert!(ControlFlow::<String, i32>::Continue(3).is_continue());
112     /// ```
113     #[inline]
114     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
115     pub fn is_continue(&self) -> bool {
116         matches!(*self, ControlFlow::Continue(_))
117     }
118
119     /// Converts the `ControlFlow` into an `Option` which is `Some` if the
120     /// `ControlFlow` was `Break` and `None` otherwise.
121     ///
122     /// # Examples
123     ///
124     /// ```
125     /// #![feature(control_flow_enum)]
126     /// use std::ops::ControlFlow;
127     ///
128     /// assert_eq!(ControlFlow::<i32, String>::Break(3).break_value(), Some(3));
129     /// assert_eq!(ControlFlow::<String, i32>::Continue(3).break_value(), None);
130     /// ```
131     #[inline]
132     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
133     pub fn break_value(self) -> Option<B> {
134         match self {
135             ControlFlow::Continue(..) => None,
136             ControlFlow::Break(x) => Some(x),
137         }
138     }
139
140     /// Maps `ControlFlow<B, C>` to `ControlFlow<T, C>` by applying a function
141     /// to the break value in case it exists.
142     #[inline]
143     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
144     pub fn map_break<T, F>(self, f: F) -> ControlFlow<T, C>
145     where
146         F: FnOnce(B) -> T,
147     {
148         match self {
149             ControlFlow::Continue(x) => ControlFlow::Continue(x),
150             ControlFlow::Break(x) => ControlFlow::Break(f(x)),
151         }
152     }
153 }
154
155 impl<R: Try> ControlFlow<R, R::Ok> {
156     /// Create a `ControlFlow` from any type implementing `Try`.
157     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
158     #[inline]
159     pub fn from_try(r: R) -> Self {
160         match Try::into_result(r) {
161             Ok(v) => ControlFlow::Continue(v),
162             Err(v) => ControlFlow::Break(Try::from_error(v)),
163         }
164     }
165
166     /// Convert a `ControlFlow` into any type implementing `Try`;
167     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
168     #[inline]
169     pub fn into_try(self) -> R {
170         match self {
171             ControlFlow::Continue(v) => Try::from_ok(v),
172             ControlFlow::Break(v) => v,
173         }
174     }
175 }
176
177 impl<B> ControlFlow<B, ()> {
178     /// It's frequently the case that there's no value needed with `Continue`,
179     /// so this provides a way to avoid typing `(())`, if you prefer it.
180     ///
181     /// # Examples
182     ///
183     /// ```
184     /// #![feature(control_flow_enum)]
185     /// use std::ops::ControlFlow;
186     ///
187     /// let mut partial_sum = 0;
188     /// let last_used = (1..10).chain(20..25).try_for_each(|x| {
189     ///     partial_sum += x;
190     ///     if partial_sum > 100 { ControlFlow::Break(x) }
191     ///     else { ControlFlow::CONTINUE }
192     /// });
193     /// assert_eq!(last_used.break_value(), Some(22));
194     /// ```
195     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
196     pub const CONTINUE: Self = ControlFlow::Continue(());
197 }
198
199 impl<C> ControlFlow<(), C> {
200     /// APIs like `try_for_each` don't need values with `Break`,
201     /// so this provides a way to avoid typing `(())`, if you prefer it.
202     ///
203     /// # Examples
204     ///
205     /// ```
206     /// #![feature(control_flow_enum)]
207     /// use std::ops::ControlFlow;
208     ///
209     /// let mut partial_sum = 0;
210     /// (1..10).chain(20..25).try_for_each(|x| {
211     ///     if partial_sum > 100 { ControlFlow::BREAK }
212     ///     else { partial_sum += x; ControlFlow::CONTINUE }
213     /// });
214     /// assert_eq!(partial_sum, 108);
215     /// ```
216     #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
217     pub const BREAK: Self = ControlFlow::Break(());
218 }