https://github.com/komar007/option_into_controlflow
Option into ControlFlow conversion
https://github.com/komar007/option_into_controlflow
rust rust-controlflow rust-no-std rust-option rust-patterns
Last synced: 4 months ago
JSON representation
Option into ControlFlow conversion
- Host: GitHub
- URL: https://github.com/komar007/option_into_controlflow
- Owner: komar007
- License: apache-2.0
- Created: 2024-12-31T10:14:24.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2024-12-31T18:40:18.000Z (about 1 year ago)
- Last Synced: 2025-09-22T05:25:40.329Z (5 months ago)
- Topics: rust, rust-controlflow, rust-no-std, rust-option, rust-patterns
- Language: Rust
- Homepage:
- Size: 19.5 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE-APACHE
Awesome Lists containing this project
README
# `option_into_controlflow`

[](https://crates.io/crates/option_into_controlflow/)

[](https://docs.rs/option_into_controlflow)
Convert `Option` into `ControlFlow` or `ControlFlow<_, T>`.
## Overview
Analogically to `ok_or` and `ok_or_else` for converting into `Result`, this crate introduces:
- `break_or`/`continue_or`,
- `break_or_else`/`continue_or_else` and
- `break_or_default`/`continue_or_default` for converting into `ControlFlow`.
Since `ControlFlow` is more symmetrical than `Result`, functions exist for converting the `Some`
variant both into `Break` and `Continue`.
## Why?
Suppose you are receiving some messages:
```rust
async fn process_messages(mut msgs: mpsc::Receiver) {
while let Some(msg) = msgs.recv().await {
println!("msg = {}", msg)
}
}
```
but now you need to support cancellation, so you do this:
```rust
async fn process_messages(mut msgs: mpsc::Receiver, token: CancellationToken) {
while let Some(msg) = select! { biased;
_ = token.cancelled() => None,
m = msgs.recv() => m,
} {
println!("msg = {}", msg)
}
}
```
and that's fine, but now you're using `Option` for controlling flow. `None` used to mean there will
be no more messages. This is semantically correct for a receiver and it's fine to pattern-match on
it in a loop condition. But now, `None` means "there will be no more messages OR processing is
cancelled". As the logic becomes more complicated, you might want to pattern-match on
`ControlFlow::Continue` instead, which is there for a reason - it conveys whether to continue or to
break.
So you can do this:
```rust
async fn process_messages(mut msgs: mpsc::Receiver, token: CancellationToken) {
while let ControlFlow::Continue(msg) = select! { biased;
_ = token.cancelled() => ControlFlow::Break(()),
m = msgs.recv() => {
if let Some(m) = m {
ControlFlow::Continue(m)
} else {
ControlFlow::Break(())
}
},
} {
println!("msg = {}", msg)
}
}
```
but it's so verbose, so, of course, you use `option_into_controlflow`:
```rust
async fn process_messages(mut msgs: mpsc::Receiver, token: CancellationToken) {
while let ControlFlow::Continue(msg) = select! { biased;
_ = token.cancelled() => ControlFlow::Break(()),
m = msgs.recv() => m.continue_or(()),
} {
println!("msg = {}", msg)
}
}
```
which is the best of both worlds - you no longer use `Option` for controlling the loop, but your
code becomes semantic, idiomatic and understandable.