Stabilize `ops::ControlFlow` (just the type)
Tracking issue: https://github.com/rust-lang/rust/issues/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 https://github.com/rust-lang/rfcs/pull/3058, where it got a bunch of positive comments (examples [1](https://github.com/rust-lang/rfcs/pull/3058#issuecomment-
758277325) [2](https://github.com/rust-lang/rfcs/pull/3058#pullrequestreview-
592106494) [3](https://github.com/rust-lang/rfcs/pull/3058#issuecomment-
784444155) [4](https://github.com/rust-lang/rfcs/pull/3058#issuecomment-
797031584)). Its use in the compiler has been well received (https://github.com/rust-lang/rust/pull/78182#issuecomment-
713695594), and there are ecosystem updates interested in using it (https://github.com/rust-itertools/itertools/issues/469#issuecomment-
677729589, https://github.com/jonhoo/rust-imap/issues/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.
lifetimes_to_define: Vec::new(),
is_collecting_in_band_lifetimes: false,
in_scope_lifetimes: Vec::new(),
- allow_try_trait: Some([sym::control_flow_enum, sym::try_trait_v2][..].into()),
+ allow_try_trait: Some([sym::try_trait_v2][..].into()),
allow_gen_future: Some([sym::gen_future][..].into()),
}
.lower_crate(krate)
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(box_patterns)]
#![feature(bool_to_option)]
-#![feature(control_flow_enum)]
#![feature(crate_visibility_modifier)]
#![feature(format_args_capture)]
#![feature(iter_zip)]
constructor,
contents,
context,
- control_flow_enum,
convert,
copy,
copy_closures,
#![feature(crate_visibility_modifier)]
#![feature(in_band_lifetimes)]
#![feature(nll)]
-#![feature(control_flow_enum)]
#![recursion_limit = "256"]
#[macro_use]
/// assert_eq!(it.len(), 2);
/// assert_eq!(it.next(), Some(&40));
/// ```
+ ///
+ /// While you cannot `break` from a closure, the [`crate::ops::ControlFlow`]
+ /// type allows a similar idea:
+ ///
+ /// ```
+ /// use std::ops::ControlFlow;
+ ///
+ /// let triangular = (1..30).try_fold(0_i8, |prev, x| {
+ /// if let Some(next) = prev.checked_add(x) {
+ /// ControlFlow::Continue(next)
+ /// } else {
+ /// ControlFlow::Break(prev)
+ /// }
+ /// });
+ /// assert_eq!(triangular, ControlFlow::Break(120));
+ ///
+ /// let triangular = (1..30).try_fold(0_u64, |prev, x| {
+ /// if let Some(next) = prev.checked_add(x) {
+ /// ControlFlow::Continue(next)
+ /// } else {
+ /// ControlFlow::Break(prev)
+ /// }
+ /// });
+ /// assert_eq!(triangular, ControlFlow::Continue(435));
+ /// ```
#[inline]
#[stable(feature = "iterator_try_fold", since = "1.27.0")]
fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R
/// // It short-circuited, so the remaining items are still in the iterator:
/// assert_eq!(it.next(), Some("stale_bread.json"));
/// ```
+ ///
+ /// The [`crate::ops::ControlFlow`] type can be used with this method for the
+ /// situations in which you'd use `break` and `continue` in a normal loop:
+ ///
+ /// ```
+ /// use std::ops::ControlFlow;
+ ///
+ /// let r = (2..100).try_for_each(|x| {
+ /// if 323 % x == 0 {
+ /// return ControlFlow::Break(x)
+ /// }
+ ///
+ /// ControlFlow::Continue(())
+ /// });
+ /// assert_eq!(r, ControlFlow::Break(17));
+ /// ```
#[inline]
#[stable(feature = "iterator_try_fold", since = "1.27.0")]
fn try_for_each<F, R>(&mut self, f: F) -> R
///
/// Early-exiting from [`Iterator::try_for_each`]:
/// ```
-/// #![feature(control_flow_enum)]
/// use std::ops::ControlFlow;
///
/// let r = (2..100).try_for_each(|x| {
///
/// A basic tree traversal:
/// ```no_run
-/// #![feature(control_flow_enum)]
/// use std::ops::ControlFlow;
///
/// pub struct TreeNode<T> {
/// }
/// }
/// ```
-#[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
+#[stable(feature = "control_flow_enum_type", since = "1.55.0")]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ControlFlow<B, C = ()> {
/// Move on to the next phase of the operation as normal.
+ #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
#[cfg_attr(not(bootstrap), lang = "Continue")]
Continue(C),
/// Exit the operation without running subsequent phases.
+ #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
#[cfg_attr(not(bootstrap), lang = "Break")]
Break(B),
// Yes, the order of the variants doesn't match the type parameters.
/// into the return type using [`Try::from_output`]:
/// ```
/// # #![feature(try_trait_v2)]
-/// # #![feature(control_flow_enum)]
/// # use std::ops::{ControlFlow, Try};
/// fn simple_try_fold_2<A, T, R: Try<Output = A>>(
/// iter: impl Iterator<Item = T>,
/// recreated from their corresponding residual, so we'll just call it:
/// ```
/// # #![feature(try_trait_v2)]
-/// # #![feature(control_flow_enum)]
/// # use std::ops::{ControlFlow, Try};
/// pub fn simple_try_fold_3<A, T, R: Try<Output = A>>(
/// iter: impl Iterator<Item = T>,
///
/// ```
/// #![feature(try_trait_v2)]
- /// #![feature(control_flow_enum)]
/// use std::ops::Try;
///
/// assert_eq!(<Result<_, String> as Try>::from_output(3), Ok(3));
///
/// ```
/// #![feature(try_trait_v2)]
- /// #![feature(control_flow_enum)]
/// use std::ops::{ControlFlow, Try};
///
/// assert_eq!(Ok::<_, String>(3).branch(), ControlFlow::Continue(3));
///
/// ```
/// #![feature(try_trait_v2)]
- /// #![feature(control_flow_enum)]
/// use std::ops::{ControlFlow, FromResidual};
///
/// assert_eq!(Result::<String, i64>::from_residual(Err(3_u8)), Err(3));
#![feature(const_ptr_read)]
#![feature(const_ptr_write)]
#![feature(const_ptr_offset)]
-#![feature(control_flow_enum)]
#![feature(core_intrinsics)]
#![feature(core_private_bignum)]
#![feature(core_private_diy_float)]