An open API service indexing awesome lists of open source software.

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

Awesome Lists containing this project

README

          

# `option_into_controlflow`

![Crates.io License](https://img.shields.io/crates/l/option_into_controlflow)
[![Crates.io Version](https://img.shields.io/crates/v/option_into_controlflow)](https://crates.io/crates/option_into_controlflow/)
![GitHub branch check runs](https://img.shields.io/github/check-runs/komar007/option_into_controlflow/main)
[![docs.rs](https://img.shields.io/docsrs/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.