Skip to content

Commit 1b7309e

Browse files
committed
Expand the docs for ops::ControlFlow a bit
Since I was writing some examples for an RFC anyway.
1 parent 08fdbd5 commit 1b7309e

File tree

4 files changed

+104
-3
lines changed

4 files changed

+104
-3
lines changed

library/core/src/ops/control_flow.rs

+83-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,63 @@
11
use crate::ops::Try;
22

3-
/// Used to make try_fold closures more like normal loops
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+
/// ```
451
#[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
552
#[derive(Debug, Clone, Copy, PartialEq)]
653
pub enum ControlFlow<B, C = ()> {
7-
/// Continue in the loop, using the given value for the next iteration
54+
/// Move on to the next phase of the operation as normal.
855
Continue(C),
9-
/// Exit the loop, yielding the given value
56+
/// Exit the operation without running subsequent phases.
1057
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.
1161
}
1262

1363
#[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
@@ -33,13 +83,33 @@ impl<B, C> Try for ControlFlow<B, C> {
3383

3484
impl<B, C> ControlFlow<B, C> {
3585
/// 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+
/// ```
3696
#[inline]
3797
#[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
3898
pub fn is_break(&self) -> bool {
3999
matches!(*self, ControlFlow::Break(_))
40100
}
41101

42102
/// 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+
/// ```
43113
#[inline]
44114
#[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
45115
pub fn is_continue(&self) -> bool {
@@ -48,6 +118,16 @@ impl<B, C> ControlFlow<B, C> {
48118

49119
/// Converts the `ControlFlow` into an `Option` which is `Some` if the
50120
/// `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+
/// ```
51131
#[inline]
52132
#[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
53133
pub fn break_value(self) -> Option<B> {

library/core/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#![feature(const_maybe_uninit_assume_init)]
1616
#![feature(const_ptr_read)]
1717
#![feature(const_ptr_offset)]
18+
#![feature(control_flow_enum)]
1819
#![feature(core_intrinsics)]
1920
#![feature(core_private_bignum)]
2021
#![feature(core_private_diy_float)]

library/core/tests/ops.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
mod control_flow;
2+
13
use core::ops::{Bound, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
24
use core::ops::{Deref, DerefMut};
35

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use core::intrinsics::discriminant_value;
2+
use core::ops::ControlFlow;
3+
4+
#[test]
5+
fn control_flow_discriminants_match_result() {
6+
// This isn't stable surface area, but helps keep `?` cheap between them,
7+
// even if LLVM can't always take advantage of it right now.
8+
// (Sadly Result and Option are inconsistent, so ControlFlow can't match both.)
9+
10+
assert_eq!(
11+
discriminant_value(&ControlFlow::<i32, i32>::Break(3)),
12+
discriminant_value(&Result::<i32, i32>::Err(3)),
13+
);
14+
assert_eq!(
15+
discriminant_value(&ControlFlow::<i32, i32>::Continue(3)),
16+
discriminant_value(&Result::<i32, i32>::Ok(3)),
17+
);
18+
}

0 commit comments

Comments
 (0)