Option type which short-cirtuits on Some instead of None in Try contexts

This commit is contained in:
Janis 2025-07-06 15:05:04 +02:00
parent b22254cb32
commit b3d4159883
3 changed files with 102 additions and 0 deletions

View file

@ -7,6 +7,7 @@ edition = "2024"
default = ["alloc"]
alloc = []
std = ["alloc"]
transposed-option = ["nightly"]
nightly = []
[dependencies]

View file

@ -1,5 +1,6 @@
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "nightly", feature(strict_provenance_atomic_ptr))]
#![cfg_attr(feature = "transposed-option", feature(try_trait_v2))]
#[cfg(any(test, feature = "std", feature = "alloc"))]
extern crate alloc;
@ -10,6 +11,8 @@ extern crate std;
pub mod atomic;
pub mod cachepadded;
pub mod drop_guard;
#[cfg(feature = "transposed-option")]
pub mod option;
pub mod ptr;
pub mod rand;
#[cfg(feature = "alloc")]

98
src/option.rs Normal file
View file

@ -0,0 +1,98 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
pub enum TransposedOption<T> {
#[default]
None,
Some(T),
}
impl<T> TransposedOption<T> {
pub fn new(value: T) -> Self {
TransposedOption::Some(value)
}
pub fn is_none(&self) -> bool {
matches!(self, TransposedOption::None)
}
pub fn map<U, F>(self, f: F) -> TransposedOption<U>
where
F: FnOnce(T) -> U,
{
use TransposedOption::*;
match self {
Some(value) => Some(f(value)),
None => None,
}
}
pub fn and_then<U, F>(self, f: F) -> TransposedOption<U>
where
F: FnOnce(T) -> TransposedOption<U>,
{
use TransposedOption::*;
match self {
Some(value) => f(value),
None => None,
}
}
}
impl<T> From<Option<T>> for TransposedOption<T> {
fn from(option: Option<T>) -> Self {
match option {
Some(value) => TransposedOption::Some(value),
None => TransposedOption::None,
}
}
}
impl<T> From<TransposedOption<T>> for Option<T> {
fn from(transposed: TransposedOption<T>) -> Self {
match transposed {
TransposedOption::Some(value) => Some(value),
TransposedOption::None => None,
}
}
}
impl<T> core::ops::Try for TransposedOption<T> {
type Output = TransposedOption<T>;
type Residual = T;
fn from_output(_: Self::Output) -> Self {
use TransposedOption::*;
None
}
fn branch(self) -> std::ops::ControlFlow<Self::Residual, Self::Output> {
use TransposedOption::*;
match self {
Some(value) => std::ops::ControlFlow::Break(value),
None => std::ops::ControlFlow::Continue(None),
}
}
}
impl<T: From<U>, U> core::ops::FromResidual<U> for TransposedOption<T> {
fn from_residual(residual: U) -> Self {
Self::new(residual.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
use TransposedOption::*;
#[test]
fn transposed_option_try() {
let a: TransposedOption<i32> = try {
TransposedOption::Some(42)?;
None::<i32>?;
Some(3)
};
assert_eq!(a, TransposedOption::Some(42));
}
}