pub enum ControlFlow<B, C = ()> {
Continue(C),
Break(B),
}
Used to tell an operation whether it should exit early or go on as usual.
This is used when exposing things (like graph traversals or visitors) where you want the user to be able to choose whether to exit early. Having the enum makes it clearer β no more wondering βwait, what did false mean again?β β and allows including a value.
Similar to Option and Result, this enum can be used with the ? operator to return immediately if the Break variant is present or otherwise continue normally with the value inside the Continue variant.
Early-exiting from Iterator::try_for_each:
use std::ops::ControlFlow;
let r = (2..100).try_for_each(|x| {
if 403 % x == 0 {
return ControlFlow::Break(x)
}
ControlFlow::Continue(())
});
assert_eq!(r, ControlFlow::Break(13));A basic tree traversal:
use std::ops::ControlFlow;
pub struct TreeNode<T> {
value: T,
left: Option<Box<TreeNode<T>>>,
right: Option<Box<TreeNode<T>>>,
}
impl<T> TreeNode<T> {
pub fn traverse_inorder<B>(&self, f: &mut impl FnMut(&T) -> ControlFlow<B>) -> ControlFlow<B> {
if let Some(left) = &self.left {
left.traverse_inorder(f)?;
}
f(&self.value)?;
if let Some(right) = &self.right {
right.traverse_inorder(f)?;
}
ControlFlow::Continue(())
}
fn leaf(value: T) -> Option<Box<TreeNode<T>>> {
Some(Box::new(Self { value, left: None, right: None }))
}
}
let node = TreeNode {
value: 0,
left: TreeNode::leaf(1),
right: Some(Box::new(TreeNode {
value: -1,
left: TreeNode::leaf(5),
right: TreeNode::leaf(2),
}))
};
let mut sum = 0;
let res = node.traverse_inorder(&mut |val| {
if *val < 0 {
ControlFlow::Break(*val)
} else {
sum += *val;
ControlFlow::Continue(())
}
});
assert_eq!(res, ControlFlow::Break(-1));
assert_eq!(sum, 6);Continue(C)
Move on to the next phase of the operation as normal.
Break(B)
Exit the operation without running subsequent phases.
impl<B, C> ControlFlow<B, C>
pub fn is_break(&self) -> bool
Returns true if this is a Break variant.
use std::ops::ControlFlow;
assert!(ControlFlow::<&str, i32>::Break("Stop right there!").is_break());
assert!(!ControlFlow::<&str, i32>::Continue(3).is_break());pub fn is_continue(&self) -> bool
Returns true if this is a Continue variant.
use std::ops::ControlFlow;
assert!(!ControlFlow::<&str, i32>::Break("Stop right there!").is_continue());
assert!(ControlFlow::<&str, i32>::Continue(3).is_continue());pub fn break_value(self) -> Option<B>
Converts the ControlFlow into an Option which is Some if the ControlFlow was Break and None otherwise.
use std::ops::ControlFlow;
assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").break_value(), Some("Stop right there!"));
assert_eq!(ControlFlow::<&str, i32>::Continue(3).break_value(), None);pub const fn break_ok(self) -> Result<B, C>
control_flow_ok #140266)
Converts the ControlFlow into an Result which is Ok if the ControlFlow was Break and Err if otherwise.
#![feature(control_flow_ok)]
use std::ops::ControlFlow;
struct TreeNode<T> {
value: T,
left: Option<Box<TreeNode<T>>>,
right: Option<Box<TreeNode<T>>>,
}
impl<T> TreeNode<T> {
fn find<'a>(&'a self, mut predicate: impl FnMut(&T) -> bool) -> Result<&'a T, ()> {
let mut f = |t: &'a T| -> ControlFlow<&'a T> {
if predicate(t) {
ControlFlow::Break(t)
} else {
ControlFlow::Continue(())
}
};
self.traverse_inorder(&mut f).break_ok()
}
fn traverse_inorder<'a, B>(
&'a self,
f: &mut impl FnMut(&'a T) -> ControlFlow<B>,
) -> ControlFlow<B> {
if let Some(left) = &self.left {
left.traverse_inorder(f)?;
}
f(&self.value)?;
if let Some(right) = &self.right {
right.traverse_inorder(f)?;
}
ControlFlow::Continue(())
}
fn leaf(value: T) -> Option<Box<TreeNode<T>>> {
Some(Box::new(Self {
value,
left: None,
right: None,
}))
}
}
let node = TreeNode {
value: 0,
left: TreeNode::leaf(1),
right: Some(Box::new(TreeNode {
value: -1,
left: TreeNode::leaf(5),
right: TreeNode::leaf(2),
})),
};
let res = node.find(|val: &i32| *val > 3);
assert_eq!(res, Ok(&5));pub fn map_break<T, F>(self, f: F) -> ControlFlow<T, C>where
F: FnOnce(B) -> T,Maps ControlFlow<B, C> to ControlFlow<T, C> by applying a function to the break value in case it exists.
pub fn continue_value(self) -> Option<C>
Converts the ControlFlow into an Option which is Some if the ControlFlow was Continue and None otherwise.
use std::ops::ControlFlow;
assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").continue_value(), None);
assert_eq!(ControlFlow::<&str, i32>::Continue(3).continue_value(), Some(3));pub const fn continue_ok(self) -> Result<C, B>
control_flow_ok #140266)
Converts the ControlFlow into an Result which is Ok if the ControlFlow was Continue and Err if otherwise.
#![feature(control_flow_ok)]
use std::ops::ControlFlow;
struct TreeNode<T> {
value: T,
left: Option<Box<TreeNode<T>>>,
right: Option<Box<TreeNode<T>>>,
}
impl<T> TreeNode<T> {
fn validate<B>(&self, f: &mut impl FnMut(&T) -> ControlFlow<B>) -> Result<(), B> {
self.traverse_inorder(f).continue_ok()
}
fn traverse_inorder<B>(&self, f: &mut impl FnMut(&T) -> ControlFlow<B>) -> ControlFlow<B> {
if let Some(left) = &self.left {
left.traverse_inorder(f)?;
}
f(&self.value)?;
if let Some(right) = &self.right {
right.traverse_inorder(f)?;
}
ControlFlow::Continue(())
}
fn leaf(value: T) -> Option<Box<TreeNode<T>>> {
Some(Box::new(Self {
value,
left: None,
right: None,
}))
}
}
let node = TreeNode {
value: 0,
left: TreeNode::leaf(1),
right: Some(Box::new(TreeNode {
value: -1,
left: TreeNode::leaf(5),
right: TreeNode::leaf(2),
})),
};
let res = node.validate(&mut |val| {
if *val < 0 {
return ControlFlow::Break("negative value detected");
}
if *val > 4 {
return ControlFlow::Break("too big value detected");
}
ControlFlow::Continue(())
});
assert_eq!(res, Err("too big value detected"));pub fn map_continue<T, F>(self, f: F) -> ControlFlow<B, T>where
F: FnOnce(C) -> T,Maps ControlFlow<B, C> to ControlFlow<B, T> by applying a function to the continue value in case it exists.
impl<T> ControlFlow<T, T>
pub const fn into_value(self) -> T
control_flow_into_value #137461)
Extracts the value T that is wrapped by ControlFlow<T, T>.
#![feature(control_flow_into_value)] use std::ops::ControlFlow; assert_eq!(ControlFlow::<i32, i32>::Break(1024).into_value(), 1024); assert_eq!(ControlFlow::<i32, i32>::Continue(512).into_value(), 512);
impl<R> ControlFlow<R, <R as Try>::Output>where
R: Try,This impl block contains no items.These are used only as part of implementing the iterator adapters.
They have mediocre names and non-obvious semantics, so arenβt
currently on a path to potential stabilization.
impl<B, C> Clone for ControlFlow<B, C>where
B: Clone,
C: Clone,fn clone(&self) -> ControlFlow<B, C>
fn clone_from(&mut self, source: &Self)
source. Read more
impl<B, C> Debug for ControlFlow<B, C>where
B: Debug,
C: Debug,fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>
impl<B, C> FromResidual<ControlFlow<B, Infallible>> for ControlFlow<B, C>
fn from_residual(residual: ControlFlow<B, Infallible>) -> ControlFlow<B, C>
try_trait_v2 #84277)
Residual type. Read more
impl<B, C> Hash for ControlFlow<B, C>where
B: Hash,
C: Hash,fn hash<__H>(&self, state: &mut __H)where
__H: Hasher,fn hash_slice<H>(data: &[Self], state: &mut H)where
H: Hasher,
Self: Sized,impl<B, C> PartialEq for ControlFlow<B, C>where
B: PartialEq,
C: PartialEq,fn eq(&self, other: &ControlFlow<B, C>) -> bool
self and other values to be equal, and is used by ==.fn ne(&self, other: &Rhs) -> bool
!=. The default implementation is almost always sufficient, and should not be overridden without very good reason.impl<B, C> Residual<C> for ControlFlow<B, Infallible>
type TryType = ControlFlow<B, C>
try_trait_v2_residual #91285)
impl<B, C> Try for ControlFlow<B, C>
type Output = C
try_trait_v2 #84277)
? when not short-circuiting.type Residual = ControlFlow<B, Infallible>
try_trait_v2 #84277)
FromResidual::from_residual as part of ? when short-circuiting. Read more
fn from_output(output: <ControlFlow<B, C> as Try>::Output) -> ControlFlow<B, C>
try_trait_v2 #84277)
Output type. Read more
fn branch(
self,
) -> ControlFlow<<ControlFlow<B, C> as Try>::Residual, <ControlFlow<B, C> as Try>::Output>try_trait_v2 #84277)
? to decide whether the operator should produce a value (because this returned ControlFlow::Continue) or propagate a value back to the caller (because this returned ControlFlow::Break). Read more
impl<B, C> Copy for ControlFlow<B, C>where
B: Copy,
C: Copy,impl<B, C> Eq for ControlFlow<B, C>where
B: Eq,
C: Eq,impl<B, C> StructuralPartialEq for ControlFlow<B, C>
impl<B, C> Freeze for ControlFlow<B, C>where
C: Freeze,
B: Freeze,impl<B, C> RefUnwindSafe for ControlFlow<B, C>where
C: RefUnwindSafe,
B: RefUnwindSafe,impl<B, C> Send for ControlFlow<B, C>where
C: Send,
B: Send,impl<B, C> Sync for ControlFlow<B, C>where
C: Sync,
B: Sync,impl<B, C> Unpin for ControlFlow<B, C>where
C: Unpin,
B: Unpin,impl<B, C> UnwindSafe for ControlFlow<B, C>where
C: UnwindSafe,
B: UnwindSafe,impl<T> Any for Twhere
T: 'static + ?Sized,impl<T> Borrow<T> for Twhere
T: ?Sized,impl<T> BorrowMut<T> for Twhere
T: ?Sized,impl<T> CloneToUninit for Twhere
T: Clone,unsafe fn clone_to_uninit(&self, dest: *mut u8)
clone_to_uninit #126799)
impl<T> From<T> for T
fn from(t: T) -> T
Returns the argument unchanged.
impl<T, U> Into<U> for Twhere
U: From<T>,fn into(self) -> U
Calls U::from(self).
That is, this conversion is whatever the implementation of From<T> for U chooses to do.
impl<T> ToOwned for Twhere
T: Clone,type Owned = T
fn to_owned(&self) -> T
fn clone_into(&self, target: &mut T)
impl<T, U> TryFrom<U> for Twhere
U: Into<T>,type Error = Infallible
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
impl<T, U> TryInto<U> for Twhere
U: TryFrom<T>,
Β© 2010 The Rust Project Developers
Licensed under the Apache License, Version 2.0 or the MIT license, at your option.
https://doc.rust-lang.org/std/ops/enum.ControlFlow.html