Skip to content

Commit 3f228b9

Browse files
JohnTitorgitbot
authored and
gitbot
committed
Rollup merge of rust-lang#85608 - scottmcm:stabilize-control-flow-enum-basics, r=m-ou-se
Stabilize `ops::ControlFlow` (just the type) Tracking issue: rust-lang#75744 (which also tracks items *not* closed by this PR). With the new `?` desugar implemented, [it's no longer possible to mix `Result` and `ControlFlow`](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=13feec97f5c96a9d791d97f7de2d49a6). (At the time of making this PR, godbolt was still on the 2021-05-01 nightly, where you can see that [the mixing example compiled](https://rust.godbolt.org/z/13Ke54j16).) That resolves the only blocker I know of, so I'd like to propose that `ControlFlow` be considered for stabilization. Its basic existence was part of rust-lang/rfcs#3058, where it got a bunch of positive comments (examples [1](rust-lang/rfcs#3058 (comment)) [2](rust-lang/rfcs#3058 (review)) [3](rust-lang/rfcs#3058 (comment)) [4](rust-lang/rfcs#3058 (comment))). Its use in the compiler has been well received (rust-lang#78182 (comment)), and there are ecosystem updates interested in using it (rust-itertools/itertools#469 (comment), jonhoo/rust-imap#194). As this will need an FCP, picking a libs member manually: r? `@m-ou-se` ## Stabilized APIs ```rust #[derive(Debug, Clone, Copy, PartialEq)] pub enum ControlFlow<B, C = ()> { /// Exit the operation without running subsequent phases. Break(B), /// Move on to the next phase of the operation as normal. Continue(C), } ``` As well as using `?` on a `ControlFlow<B, _>` in a function returning `ControlFlow<B, _>`. (Note, in particular, that there's no `From::from`-conversion on the `Break` value, the way there is for `Err`s.) ## Existing APIs *not* stabilized here All the associated methods and constants: `break_value`, `is_continue`, `map_break`, [`CONTINUE`](https://doc.rust-lang.org/nightly/std/ops/enum.ControlFlow.html#associatedconstant.CONTINUE), etc. Some of the existing methods in nightly seem reasonable, some seem like they should be removed, and some need more discussion to decide. But none of them are *essential*, so [as in the RFC](https://rust-lang.github.io/rfcs/3058-try-trait-v2.html#methods-on-controlflow), they're all omitted from this PR. They can be considered separately later, as further usage demonstrates which are important.
2 parents d54382f + 3008efe commit 3f228b9

File tree

4 files changed

+44
-9
lines changed

4 files changed

+44
-9
lines changed

core/src/iter/traits/iterator.rs

+41
Original file line numberDiff line numberDiff line change
@@ -1959,6 +1959,31 @@ pub trait Iterator {
19591959
/// assert_eq!(it.len(), 2);
19601960
/// assert_eq!(it.next(), Some(&40));
19611961
/// ```
1962+
///
1963+
/// While you cannot `break` from a closure, the [`crate::ops::ControlFlow`]
1964+
/// type allows a similar idea:
1965+
///
1966+
/// ```
1967+
/// use std::ops::ControlFlow;
1968+
///
1969+
/// let triangular = (1..30).try_fold(0_i8, |prev, x| {
1970+
/// if let Some(next) = prev.checked_add(x) {
1971+
/// ControlFlow::Continue(next)
1972+
/// } else {
1973+
/// ControlFlow::Break(prev)
1974+
/// }
1975+
/// });
1976+
/// assert_eq!(triangular, ControlFlow::Break(120));
1977+
///
1978+
/// let triangular = (1..30).try_fold(0_u64, |prev, x| {
1979+
/// if let Some(next) = prev.checked_add(x) {
1980+
/// ControlFlow::Continue(next)
1981+
/// } else {
1982+
/// ControlFlow::Break(prev)
1983+
/// }
1984+
/// });
1985+
/// assert_eq!(triangular, ControlFlow::Continue(435));
1986+
/// ```
19621987
#[inline]
19631988
#[stable(feature = "iterator_try_fold", since = "1.27.0")]
19641989
fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R
@@ -2001,6 +2026,22 @@ pub trait Iterator {
20012026
/// // It short-circuited, so the remaining items are still in the iterator:
20022027
/// assert_eq!(it.next(), Some("stale_bread.json"));
20032028
/// ```
2029+
///
2030+
/// The [`crate::ops::ControlFlow`] type can be used with this method for the
2031+
/// situations in which you'd use `break` and `continue` in a normal loop:
2032+
///
2033+
/// ```
2034+
/// use std::ops::ControlFlow;
2035+
///
2036+
/// let r = (2..100).try_for_each(|x| {
2037+
/// if 323 % x == 0 {
2038+
/// return ControlFlow::Break(x)
2039+
/// }
2040+
///
2041+
/// ControlFlow::Continue(())
2042+
/// });
2043+
/// assert_eq!(r, ControlFlow::Break(17));
2044+
/// ```
20042045
#[inline]
20052046
#[stable(feature = "iterator_try_fold", since = "1.27.0")]
20062047
fn try_for_each<F, R>(&mut self, f: F) -> R

core/src/ops/control_flow.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ use crate::{convert, ops};
1111
///
1212
/// Early-exiting from [`Iterator::try_for_each`]:
1313
/// ```
14-
/// #![feature(control_flow_enum)]
1514
/// use std::ops::ControlFlow;
1615
///
1716
/// let r = (2..100).try_for_each(|x| {
@@ -26,7 +25,6 @@ use crate::{convert, ops};
2625
///
2726
/// A basic tree traversal:
2827
/// ```no_run
29-
/// #![feature(control_flow_enum)]
3028
/// use std::ops::ControlFlow;
3129
///
3230
/// pub struct TreeNode<T> {
@@ -48,13 +46,15 @@ use crate::{convert, ops};
4846
/// }
4947
/// }
5048
/// ```
51-
#[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
49+
#[stable(feature = "control_flow_enum_type", since = "1.55.0")]
5250
#[derive(Debug, Clone, Copy, PartialEq)]
5351
pub enum ControlFlow<B, C = ()> {
5452
/// Move on to the next phase of the operation as normal.
53+
#[stable(feature = "control_flow_enum_type", since = "1.55.0")]
5554
#[cfg_attr(not(bootstrap), lang = "Continue")]
5655
Continue(C),
5756
/// Exit the operation without running subsequent phases.
57+
#[stable(feature = "control_flow_enum_type", since = "1.55.0")]
5858
#[cfg_attr(not(bootstrap), lang = "Break")]
5959
Break(B),
6060
// Yes, the order of the variants doesn't match the type parameters.

core/src/ops/try_trait.rs

-5
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ use crate::ops::ControlFlow;
5555
/// into the return type using [`Try::from_output`]:
5656
/// ```
5757
/// # #![feature(try_trait_v2)]
58-
/// # #![feature(control_flow_enum)]
5958
/// # use std::ops::{ControlFlow, Try};
6059
/// fn simple_try_fold_2<A, T, R: Try<Output = A>>(
6160
/// iter: impl Iterator<Item = T>,
@@ -79,7 +78,6 @@ use crate::ops::ControlFlow;
7978
/// recreated from their corresponding residual, so we'll just call it:
8079
/// ```
8180
/// # #![feature(try_trait_v2)]
82-
/// # #![feature(control_flow_enum)]
8381
/// # use std::ops::{ControlFlow, Try};
8482
/// pub fn simple_try_fold_3<A, T, R: Try<Output = A>>(
8583
/// iter: impl Iterator<Item = T>,
@@ -170,7 +168,6 @@ pub trait Try: FromResidual {
170168
///
171169
/// ```
172170
/// #![feature(try_trait_v2)]
173-
/// #![feature(control_flow_enum)]
174171
/// use std::ops::Try;
175172
///
176173
/// assert_eq!(<Result<_, String> as Try>::from_output(3), Ok(3));
@@ -202,7 +199,6 @@ pub trait Try: FromResidual {
202199
///
203200
/// ```
204201
/// #![feature(try_trait_v2)]
205-
/// #![feature(control_flow_enum)]
206202
/// use std::ops::{ControlFlow, Try};
207203
///
208204
/// assert_eq!(Ok::<_, String>(3).branch(), ControlFlow::Continue(3));
@@ -329,7 +325,6 @@ pub trait FromResidual<R = <Self as Try>::Residual> {
329325
///
330326
/// ```
331327
/// #![feature(try_trait_v2)]
332-
/// #![feature(control_flow_enum)]
333328
/// use std::ops::{ControlFlow, FromResidual};
334329
///
335330
/// assert_eq!(Result::<String, i64>::from_residual(Err(3_u8)), Err(3));

core/tests/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
#![feature(const_ptr_read)]
1616
#![feature(const_ptr_write)]
1717
#![feature(const_ptr_offset)]
18-
#![feature(control_flow_enum)]
1918
#![feature(core_intrinsics)]
2019
#![feature(core_private_bignum)]
2120
#![feature(core_private_diy_float)]

0 commit comments

Comments
 (0)