From 486ce4453f807d7e9a37626f906213446b79caad Mon Sep 17 00:00:00 2001 From: Dmitrii Demenev Date: Mon, 16 Sep 2024 14:59:53 -0600 Subject: [PATCH 1/5] Added either::map_both --- src/lib.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index ec1630c..8d59b77 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,6 +60,8 @@ pub enum Either { /// /// Syntax: `either::for_both!(` *expression* `,` *pattern* `=>` *expression* `)` /// +/// Unlike [`map_both!`], this macro converges both variants to the type returned by the expression. +/// /// # Example /// /// ``` @@ -87,6 +89,37 @@ macro_rules! for_both { }; } +/// Evaluate the provided expression for both [`Either::Left`] and [`Either::Right`], +/// returning an [`Either`] with the results. +/// +/// This macro is useful in cases where both sides of [`Either`] can be interacted with +/// in the same way even though the don't share the same type. +/// +/// Syntax: `either::map_both!(` *expression* `,` *pattern* `=>` *expression* `)` +/// +/// Unlike [`for_both!`], this macro returns an [`Either`] with the results of the expressions. +/// +/// # Example +/// +/// ``` +/// use either::Either; +/// +/// struct Wrapper(T); +/// +/// fn wrap(owned_or_borrowed: Either) -> Either, Wrapper<&'static str>> { +/// either::map_both!(owned_or_borrowed, s => Wrapper(s)) +/// } +/// ``` +#[macro_export] +macro_rules! map_both { + ($value:expr, $pattern:pat => $result:expr) => { + match $value { + $crate::Either::Left($pattern) => $crate::Either::Left($result), + $crate::Either::Right($pattern) => $crate::Either::Right($result), + } + }; +} + /// Macro for unwrapping the left side of an [`Either`], which fails early /// with the opposite side. Can only be used in functions that return /// `Either` because of the early return of `Right` that it provides. From 7ab14bc267a7f9c9df984e1bfd98f29b2c15bc93 Mon Sep 17 00:00:00 2001 From: Dmitrii Demenev Date: Mon, 16 Sep 2024 15:46:08 -0600 Subject: [PATCH 2/5] Added either::try_map_both --- src/lib.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 8d59b77..a7ca3b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -120,6 +120,41 @@ macro_rules! map_both { }; } +/// Evaluate the provided expression for both [`Either::Left`] and [`Either::Right`], +/// returning a [Result] where the [`Ok`] variant is an [`Either`] with the results. +/// +/// This macro is useful in cases where both sides of [`Either`] can be interacted with +/// in the same way even though the don't share the same type. +/// +/// `either::map_both!(` *expression* `,` *pattern* `=>` *expression* `)` +/// +/// Unlike [`map_both!`], this macro returns a [Result] where the [`Ok`] variant is an [`Either`] with the results. +/// +/// # Example +/// +/// ``` +/// use either::Either; +/// +/// fn wrap(owned_or_borrowed: Either) -> Result, ()> { +/// either::try_map_both!(owned_or_borrowed, s => Ok(s)) +/// } +/// ``` +#[macro_export] +macro_rules! try_map_both { + ($value:expr, $pattern:pat => $result:expr) => { + match $value { + $crate::Either::Left($pattern) => match $result { + Ok(ok) => Ok($crate::Either::Left(ok)), + Err(err) => Err(err), + }, + $crate::Either::Right($pattern) => match $result { + Ok(ok) => Ok($crate::Either::Right(ok)), + Err(err) => Err(err), + }, + } + }; +} + /// Macro for unwrapping the left side of an [`Either`], which fails early /// with the opposite side. Can only be used in functions that return /// `Either` because of the early return of `Right` that it provides. From 58c8eefb44aa022ccc9a85a251aed5be88f3052c Mon Sep 17 00:00:00 2001 From: Dmitrii Demenev Date: Tue, 17 Sep 2024 22:25:46 -0600 Subject: [PATCH 3/5] Provided a better implementation of try_map_both --- src/lib.rs | 82 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a7ca3b9..94eedf2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -130,7 +130,9 @@ macro_rules! map_both { /// /// Unlike [`map_both!`], this macro returns a [Result] where the [`Ok`] variant is an [`Either`] with the results. /// -/// # Example +/// # Examples +/// +/// Works with [`Result`]: /// /// ``` /// use either::Either; @@ -139,18 +141,39 @@ macro_rules! map_both { /// either::try_map_both!(owned_or_borrowed, s => Ok(s)) /// } /// ``` +/// +/// Works with [`Option`]: +/// +/// ``` +/// use either::Either; +/// +/// fn wrap(owned_or_borrowed: Either) -> Option> { +/// either::try_map_both!(owned_or_borrowed, s => Some(s)) +/// } +/// ``` +/// +/// If you want to see another [`Try`](https://doc.rust-lang.org/beta/std/ops/trait.Try.html) type +/// supported, please open an issue or a PR. #[macro_export] macro_rules! try_map_both { ($value:expr, $pattern:pat => $result:expr) => { match $value { - $crate::Either::Left($pattern) => match $result { - Ok(ok) => Ok($crate::Either::Left(ok)), - Err(err) => Err(err), - }, - $crate::Either::Right($pattern) => match $result { - Ok(ok) => Ok($crate::Either::Right(ok)), - Err(err) => Err(err), - }, + $crate::Either::Left($pattern) => { + let f = move || { + let result = $result?; + let e = $crate::Either::Left(result); + <_ as $crate::__either_internals::Tryish>::from_output(e) + }; + f() + } + $crate::Either::Right($pattern) => { + let f = move || { + let result = $result?; + let e = $crate::Either::Right(result); + <_ as $crate::__either_internals::Tryish>::from_output(e) + }; + f() + } } }; } @@ -1641,3 +1664,44 @@ fn _unsized_std_propagation() { check_t!(::std::ffi::OsStr); check_t!(::std::ffi::CStr); } + +mod private { + /// The trait that allows [sealing] the hidden [`Tryish`](super::__either_internals::Tryish) + /// trait, which is used in [`try_map_both`] macro. + /// + /// [sealing]: https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed + pub trait TryishSealed {} +} + +#[doc(hidden)] +pub mod __either_internals { + use super::private::TryishSealed; + + /// The minimal interface for a type to be used with [`try_map_both`] + /// while [`Try`](https://doc.rust-lang.org/beta/std/ops/trait.Try.html) + /// trait is unstable. + pub trait Tryish: TryishSealed { + type Output; + + fn from_output(output: Self::Output) -> Self; + } + + impl TryishSealed for Result {} + impl TryishSealed for Option {} + + impl Tryish for Option { + type Output = T; + + fn from_output(output: Self::Output) -> Self { + Some(output) + } + } + + impl Tryish for Result { + type Output = T; + + fn from_output(output: Self::Output) -> Self { + Ok(output) + } + } +} From cdbfde35f407381340ed6f8081862c2fb7f7ded8 Mon Sep 17 00:00:00 2001 From: Dmitrii Demenev Date: Tue, 17 Sep 2024 22:32:37 -0600 Subject: [PATCH 4/5] Replaced uses of internal map_either with newly added map_both --- src/iterator.rs | 10 +++++----- src/lib.rs | 21 ++++++--------------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/src/iterator.rs b/src/iterator.rs index 9c5a83f..09bf22c 100644 --- a/src/iterator.rs +++ b/src/iterator.rs @@ -185,7 +185,7 @@ where type Item = Either; fn next(&mut self) -> Option { - Some(map_either!(self.inner, ref mut inner => inner.next()?)) + Some(map_both!(self.inner, ref mut inner => inner.next()?)) } fn size_hint(&self) -> (usize, Option) { @@ -211,11 +211,11 @@ where } fn last(self) -> Option { - Some(map_either!(self.inner, inner => inner.last()?)) + Some(map_both!(self.inner, inner => inner.last()?)) } fn nth(&mut self, n: usize) -> Option { - Some(map_either!(self.inner, ref mut inner => inner.nth(n)?)) + Some(map_both!(self.inner, ref mut inner => inner.nth(n)?)) } fn collect(self) -> B @@ -275,11 +275,11 @@ where R: DoubleEndedIterator, { fn next_back(&mut self) -> Option { - Some(map_either!(self.inner, ref mut inner => inner.next_back()?)) + Some(map_both!(self.inner, ref mut inner => inner.next_back()?)) } fn nth_back(&mut self, n: usize) -> Option { - Some(map_either!(self.inner, ref mut inner => inner.nth_back(n)?)) + Some(map_both!(self.inner, ref mut inner => inner.nth_back(n)?)) } fn rfold(self, init: Acc, f: G) -> Acc diff --git a/src/lib.rs b/src/lib.rs index 94eedf2..f2db2f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -221,15 +221,6 @@ macro_rules! try_right { }; } -macro_rules! map_either { - ($value:expr, $pattern:pat => $result:expr) => { - match $value { - Left($pattern) => Left($result), - Right($pattern) => Right($result), - } - }; -} - mod iterator; pub use self::iterator::IterEither; @@ -630,7 +621,7 @@ impl Either { L: IntoIterator, R: IntoIterator, { - map_either!(self, inner => inner.into_iter()) + map_both!(self, inner => inner.into_iter()) } /// Borrow the inner value as an iterator. @@ -653,7 +644,7 @@ impl Either { for<'a> &'a L: IntoIterator, for<'a> &'a R: IntoIterator::Item>, { - map_either!(self, inner => inner.into_iter()) + map_both!(self, inner => inner.into_iter()) } /// Mutably borrow the inner value as an iterator. @@ -684,7 +675,7 @@ impl Either { for<'a> &'a mut L: IntoIterator, for<'a> &'a mut R: IntoIterator::Item>, { - map_either!(self, inner => inner.into_iter()) + map_both!(self, inner => inner.into_iter()) } /// Converts an `Either` of `Iterator`s to be an `Iterator` of `Either`s @@ -708,7 +699,7 @@ impl Either { L: IntoIterator, R: IntoIterator, { - IterEither::new(map_either!(self, inner => inner.into_iter())) + IterEither::new(map_both!(self, inner => inner.into_iter())) } /// Borrows an `Either` of `Iterator`s to be an `Iterator` of `Either`s @@ -732,7 +723,7 @@ impl Either { for<'a> &'a L: IntoIterator, for<'a> &'a R: IntoIterator, { - IterEither::new(map_either!(self, inner => inner.into_iter())) + IterEither::new(map_both!(self, inner => inner.into_iter())) } /// Mutably borrows an `Either` of `Iterator`s to be an `Iterator` of `Either`s @@ -758,7 +749,7 @@ impl Either { for<'a> &'a mut L: IntoIterator, for<'a> &'a mut R: IntoIterator, { - IterEither::new(map_either!(self, inner => inner.into_iter())) + IterEither::new(map_both!(self, inner => inner.into_iter())) } /// Return left value or given value From dc29da2c214c8a474e40f12d5c502bbcc824f784 Mon Sep 17 00:00:00 2001 From: Dmitrii Demenev Date: Tue, 17 Sep 2024 22:37:33 -0600 Subject: [PATCH 5/5] Fixed the docs of try_map_both! --- src/lib.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f2db2f9..b879542 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -121,14 +121,15 @@ macro_rules! map_both { } /// Evaluate the provided expression for both [`Either::Left`] and [`Either::Right`], -/// returning a [Result] where the [`Ok`] variant is an [`Either`] with the results. +/// returning a `T`: [`Try`] where the [`Try::Output`] variant is an [`Either`] with outputs. /// /// This macro is useful in cases where both sides of [`Either`] can be interacted with /// in the same way even though the don't share the same type. /// -/// `either::map_both!(` *expression* `,` *pattern* `=>` *expression* `)` +/// `either::try_map_both!(` *expression* `,` *pattern* `=>` *expression* `)` /// -/// Unlike [`map_both!`], this macro returns a [Result] where the [`Ok`] variant is an [`Either`] with the results. +/// Unlike [`map_both!`], this macro returns a [`Try`] where the [`Try::Output`] variant is an +/// [`Either`] with the outputs. /// /// # Examples /// @@ -154,6 +155,9 @@ macro_rules! map_both { /// /// If you want to see another [`Try`](https://doc.rust-lang.org/beta/std/ops/trait.Try.html) type /// supported, please open an issue or a PR. +/// +/// [`Try`]: https://doc.rust-lang.org/beta/std/ops/trait.Try.html +/// [`Try::Output`]: https://doc.rust-lang.org/beta/std/ops/trait.Try.html#associatedtype.Output #[macro_export] macro_rules! try_map_both { ($value:expr, $pattern:pat => $result:expr) => {