rayon-rs / either Goto Github PK
View Code? Open in Web Editor NEWThe enum Either with variants Left and Right is a general purpose sum type with two cases.
Home Page: https://docs.rs/either/
License: Apache License 2.0
The enum Either with variants Left and Right is a general purpose sum type with two cases.
Home Page: https://docs.rs/either/
License: Apache License 2.0
How about implementing Tokio traits for Either
? Not there is a third-party crate for it, but it's kinda ecosystem fragmentation, isn't it?
This might not go anywhere, but here seemed like the right place to open the conversation.
I think there is a need for a type that captures the possibility of being either owned or some kind of reference, but without requiring the ToOwned
constraint of Cow
.
Currently Either
has this Deref
impl:
impl<L, R> Deref for Either<L, R>
where
L: Deref,
R: Deref<Target = L::Target>,
Which is nice for Either<&T, Box<T>>
or even things like Either<Ref<'a, T>, Either<Rc<T>, Box<T>>>
etc. But Deref
isn't implemented for Either<T, &T>
which is a shame, because Cow
also won't work here unless T: ToOwned<Owned = T>
, which it mostly isn't unless T: Clone
. An impl for Either
that would work there would look like this:
impl<L, R> Deref for Either<L, R>
where
R: Deref<Target = L>,
But it can't co-exist with the current implementation.
A workaround is to introduce a newtype:
struct Own<T>(T);
impl<T> Deref for Own<T> {
type Target = T;
fn deref(&self) -> Self::Target {
self.0
}
}
So you can do now use Either<Own<T>, &T>
.
I'm sure many other crates have rolled their own (though I didn't find any), but it would be better if there was one canonical way to do this. What's the best way to achieve that? Try to do something in either
, some other crate or a completely new one?
Cargo.toml has what seems to me to be the right thing from the documentation:
[dependencies.either]
version = "1.5.0"
default-features = false
and yet cargo build complains, in a no_std project:
Compiling either v1.5.0
error[E0463]: can't find crate for `std`
|
= note: the `thumbv7m-none-eabi` target may not be installed
error: aborting due to previous error
For more information about this error, try `rustc --explain E0463`.
error: Could not compile `either`.
Disclaimer: I'm a newb, and probably doing something dumb, but I can't see what. Today's nightly, in case it matters.
Inspired by serde-rs/serde#2584, I looked through my dep tree and saw that either includes serde
with the "derive" feature.
Unfortunately, it's not so simple how to remove the feature here.
Either uses serde for 3 things,
For 1, We can write a manual implementation of the de/serializers with no problem. For 2 and 3, the derived deserializer makes use of doc(hidden) APIs in order to cache the serde
tree in case it needs to perform a second deserialisation.
serde_derive
directlydtolnay was initially against this idea because the versions of serde/derive need to be in lockstep.
There seems to be some effort to ensure it using some fancy target trickery. This is in the latest release of 1.0.186.
The trouble here is that either has 1 feature, serde
. For compat with older resolvers, I think I would need to rename the serde package to serde_pkg
and make the serde feature include both serde_pkg
and serde_derive
Should I make a PR with the second option? The first option is possible but it requires a lot more effort to duplicate the private APIs
At the moment the impl Iterator for Either
and into_iter
both require that L
and R
are iterators over the same type. I think that makes sense as it covers the nicest use case, but it does not cover cases where they are different.
Some examples that would be nice to iterate over:
Either<Vec<i64>, &[i64]>
Either<&[i32], &[i64]>
Either<Range<u64>, &[i64]>
I suggest adding a factor_iter
method, wich takes Either<Iter<L>, Iter<R>>
and converts it to a Iter<Either<L, R>>
.
Ideally I was hoping for something that would do something like into_iter_into(self: Either<IntoIter<L: Into<T>>, IntoIter<R: Into<T>>>) -> Iter<T>
but the myriad of Rust conversion traits (From
, Into
, AsRef
, Borrow
, Copy
) are implemented on different types, and there are no blanket implementions (and can't be until there is trait specialisation).
For example u32: Into<u64>
but &u32
is not, so it would work on Either<Vec<u32>, Vec<u64>>
but not Either<&[u32], &[u64]>
. Aslo since there are a pile of Into
impls for everything the type inferenceing never works and you need to tell it what T is.
There is one other Iterator themed method that I do think merits adding which is an analog to the iter()
method from Vec
as it does not always make sense to consume the data just to iterate over it.
It is only a simple wrapper for self.as_ref().into_iter()
but I think it is nice to have it in the docs.
bimap
is a function from the Bifunctor typeclass commonly seen implemented for the Either type.
This library already has map_left
and map_right
, so the implementation would simply be a combination of the two. Would the maintainers be interested in adding this combinator?
Example as an extension:
trait EitherExt {
type L;
type R;
fn bimap<M, S, F: Fn(Self::L) -> M, G: Fn(Self::R) -> S>(
self,
f: F,
g: G,
) -> Either<M, S>;
}
impl<L, R> EitherExt for Either<L, R> {
type L = L;
type R = R;
fn bimap<M, S, F: Fn(Self::L) -> M, G: Fn(Self::R) -> S>(
self,
f: F,
g: G,
) -> Either<M, S> {
self.map_left(f).map_right(g)
}
}
In the futures
crate, there exists their own Either
type currently. The crate commonly creates an Either
over two pairs. I'd like to add methods which factor out common types from both sides:
Either<(T, A), (U, A)> -> (A, Either<T, U>)
Either<(A, T), (A, U)> -> (A, Either<T, U>)
Either<A, A> -> A
Would these be suitable here or do you think they're better as standalone functions in futures
? The last one probably makes the most sense here at least.
Apache-2.0 license requires license file to be distributed along with sources. And MIT license would not hurt to have in archives as well.
Hi! This is a completely crazy idea and is almost centrally a won't fix, because either
is 1.0
, but I decided to log it anyway.
I've recently saw an enum Either<T, U> { A(T), B(U) }
in warp and now I think that Haskell's left/right terminology is a mistake :D Here are the benefits of A/B
.0
, .1
for pairs and ::A
, ::B
for co-pairsA
and B
have less semantic meaning than Left
, Right
.enum ABC<T, U, V> { A(T), B(U), C(V) }
.EDIT: apparently, there's no ab
crate yet 🤔
Either
can gain even more optimized iterator methods by overriding the try_fold
method added in 1.27; however, this will cause a pretty big bump in the minimum supported rust version (which appears to be 1.12 based on CI test configuration).
Hello.
I am curious if this was discussed or considered before. I searched the issues and PRs (for "bool") and found nothing similar.
Basically something like this:
trait Eitherable {
fn either<L, R>(&self, left: L, right: R) -> Either<L, R>;
fn either_else<L, FL: FnOnce() -> L, R, FR: FnOnce() -> R>(
&self,
l_func: FL,
r_func: FR,
) -> Either<L, R>;
}
impl Eitherable for bool {
fn either<L, R>(&self, left: L, right: R) -> Either<L, R> {
if *self {
Left(left)
} else {
Right(right)
}
}
fn either_else<L, FL: FnOnce() -> L, R, FR: FnOnce() -> R>(
&self,
l_func: FL,
r_func: FR,
) -> Either<L, R> {
if *self {
Left(l_func())
} else {
Right(r_func())
}
}
}
which allows nice one liner constructors like this :
my_bool.either(left_value, right_value)
instead of
if my_bool {
Either::Left(left_value)
} else {
Either::Right(right_balue)
}
Similarly for Options
Hi everyone,
I've just hit an issue while trying to replace an impl AsRef
with a concrete Either
type in a return position, here's an example: https://play.rust-lang.org/?gist=1f4771605ae47ea7e4e10881a994a2dc&version=stable&mode=debug&edition=2015
I believe it happens because Either::as_ref
method effectively hides the AsRef::as_ref
method :(
Shouldn't it be renamed to something like to_ref
so it doesn't overlap with the AsRef
trait, or is there any reason behind this specific name? The same goes for as_mut
and probably for into_iter
as well.
Clippy complains:
warning: defining a method called `into_iter` on this type; consider implementing the `std::iter::IntoIterator` trait or choosing a less ambiguous name
--> src/main.rs:373:5
|
373 | / pub fn into_iter(self) -> Either<L::IntoIter, R::IntoIter>
374 | | where L: IntoIterator,
375 | | R: IntoIterator<Item = L::Item>
376 | | {
... |
380 | | }
381 | | }
| |_____^
and I think it is right. What is the purpose of not implementing IntoIterator
?
Think the way serde is derived is a non-intuitive way. As I think most people do not want the left and right tags for the values.
Suggest adding untagged to the serde derive. However this would be a breaking change. I do not belive many people are using the serde fetaure as its now implemented.
This will allow us to do something like:
type ComplicatedType = HashMap<String, Either<Vec<String>, String>>
Some json that would be parsed now would be
{
"some_key": "hello",
"some_other_random_key": ["hello","world!"]
}
An Option<T>
is equivalent to Either<T, ()>
, so a conversion like: impl<T> From<Option<T>> for Either<T, ()>
would be nice. This can be useful for generic algorithms.
relevant rust-embedded/svd2rust#182 (comment)
There is an into_inner()
method on Either<T, T>
that can "unwrap" the inner value as long as both arms have the same type.
This is useful, but a more generic conversion could be implemented for any Either<A, B>
to any type T
that implements From<A> + From<B>
.
Such a trait would be simple to blanket implement:
pub trait FromEither<A, B> {
fn from_either(either: Either<A, B>) -> Self;
}
impl<A, B, T> FromEither<A, B> for T
where
T: From<A> + From<B>,
{
fn from_either(either: Either<A, B>) -> Self {
match either {
Left(a) => Self::from(a),
Right(b) => Self::from(b),
}
}
}
Programmers would rarely implement this directly, but rely on the blanket implementation to kick in when they've implemented From
for each arm of an Either
on their type.
Why would this be useful? Consider an Iterator
of Either
values. One may with to transform those values into another enum:
#[derive(Debug)]
struct A;
#[derive(Debug)]
struct B;
#[derive(Debug)]
enum C {
A(A),
B(B),
}
impl From<A> for C {
fn from(a: A) -> Self {
C::A(a)
}
}
impl From<B> for C {
fn from(b: B) -> Self {
C::B(b)
}
}
fn main() {
[Left(A), Right(B), Left(A), Right(B)]
.into_iter()
.map(C::from_either)
.for_each(|c| {
println!("{c:?}");
});
}
If the arms are the same, we could use Either::into_inner
as the function passed to map()
. FromEither
is useful specifically if the arms are different but still convertible to a common type.
My current workaround is to use map(|ab| ab.either(C::from, C::from))
in situations like this, which works, but is a little more verbose than map(C::from_either)
.
It would be useful to have this kind of method:
impl <L, R> Either<L, R> {
fn into_common<T>(self) -> T where T: From<L> + From<R> {
match self {
Either::Left(l) => l.into(),
Either::Right(r) => r.into(),
}
}
}
Does it sound a good idea? If so, I'll send a PR. I'm wondering what function name would be nice.
It's all very generic, so for users ending up at the documentation it's not entirely clear what this is actually good for. Why would one want a type that is like Result
but unbiased?
No need to answer that in the issue, I know but having this in the documentation in an understandable way would be useful :)
I think the Either crate is really good, and here are some methods I would love to see:
flatten
I think there should be a flatten method, like for std::Result
and std::Option
:
impl<L,R> Either<Either<L,R>, Either<L,R>> {
pub fn flatten(self) -> Either<L,R> {
match self {
Left(x) | Right(x) => x
}
}
}
As you can see from the implementation, this is the same as into_inner
but I think it is good to have this for two reasons:
One alternative may be to let flatten
use any Either<T,T>
(and thus skip combine
).
flatten_right
/flatten_left
Two more propsed functions are flatten_right
and flatten_left
:
impl<L,R> Either<Either<L,R>, R> {
pub fn flatten_left(self) -> Either<L,R> {
match self {
Left(x) => x,
Right(x) => Right(x)
}
}
}
impl<L,R> Either<L, Either<L,R>> {
pub fn flatten_right(self) -> Either<L,R> {
match self {
Right(x) => Right(x),
Left(x) => x
}
}
}
flatten_left
is just flatten
on std::Result
, but it is nice to have corresponding left and right methods to strengthen the intuition that there is nothing special with the left or the right variant, they are both considered equal.
I am fine with doing the implementation itself but I would be glad about comments, naming suggestions and general suggestions! Do you think this would be useful? Maybe it would be confusing with flatten and into_inner doing the same thing so I am fine with dropping that, but flatten_left and flatten_right seems useful imo.
Apparently AsRef
implementation of Either
lacks an "unsized" marker ?Sized
on Target
. It prevents of implementing AsRef<str>
for the Either
, for instance.
While default Either
doesn't compile (without the marker on playground) ...
extern crate either; // 1.5.0
use either::Either;
fn get_default(s: Option<impl AsRef<str>>) -> impl AsRef<str> {
s.map(Either::Left).unwrap_or_else(|| Either::Right("default"))
}
fn main() {
println!("{}", get_default(None::<String>).as_ref());
println!("{}", get_default(Some("not default")).as_ref());
}
... an example with the marker compiles just ok :) with the marker on playground
enum Either2<L, R> {
Left(L),
Right(R),
}
impl<L, R, T: ?Sized> AsRef<T> for Either2<L, R>
where
L: AsRef<T>,
R: AsRef<T>,
{
fn as_ref(&self) -> &T {
match self {
Either2::Left(x) => x.as_ref(),
Either2::Right(x) => x.as_ref(),
}
}
}
fn get_default(s: Option<impl AsRef<str>>) -> impl AsRef<str> {
s.map(Either2::Left)
.unwrap_or_else(|| Either2::Right("default"))
}
fn main() {
println!("{}", get_default(None::<String>).as_ref());
println!("{}", get_default(Some("not default")).as_ref());
}
So, my question is: was the ?Sized
marker omitted on purpose, or should I make a PR which adds the marker? :)
Coming from rust-itertools/itertools#881 - methods that let you apply other methods conditionally would be helpful.
Something that lets you do:
fn u8_to_bools(value: u8, rtl: bool) -> [bool; 8] {
let mut result = [false; 8];
result
.iter_mut()
// Applies `Iterator::rev` if and only if `rtl` is `true`.
// Otherwise does nothing.
.apply_if(rtl, Iterator::rev)
.enumerate()
.for_each(|(i, b)| *b = ((value >> i) & 1) == 1);
result
}
Along with other methods for ternary conditions, etc., like:
fn get_pairs(bytes: &[u8], overlap: bool) -> Vec<(u8, u8)> {
bytes
.iter()
.copied()
// Applies `Itertools::tuple_windows` if and only if the `overlap` is `true`.
// Otherwise applies `Itertools::tuples`.
.apply_either(overlap, Itertools::tuple_windows, Itertools::tuples)
.collect()
}
While these examples revolve around iterators, the scope itself of these conditionally applied methods is much larger and in my humble opinion very useful.
It's simple to implement and could be very useful, especially when you have a large method chain and breaking it up for an if[-else] block would negatively affect the readability (see my doc examples).
I have already written a simple implementation on my local fork of either
. Correct me if I'm wrong, but this only needs minor changes to work with the either
crate instead.
Sorry for being paranoid, but @jswrenn appears to be an owner of this crate on crates.io: https://crates.io/crates/either
What's odd is that I cannot find a commit from him in this repository. Moreover, I don't see either
among the repositories in his profile: https://github.com/jswrenn?tab=repositories&q=either&type=&language=&sort=
Is it possible he was added as an owner by mistake?
(@jswrenn Sorry for pinging you, and sorry for speaking about you in the third person.)
I'm working on my own crate https://github.com/cramertj/EitherN-rs to allow for Either
over more than two types (e.g Either4
with variants One
, Two
, Three
, and Four
). I wanted to check with @bluss and the other contributors. As my library's functionality will share many implementation details with this one, I wanted to make sure no one here feels like I'm stealing their hard work. Let me know if you have any problems or would prefer I do anything differently. Thanks!
Not sure whether this is in scope for this library but in rust-libp2p
, we have a custom Either
type because we need pin projection.
Would you accept a PR for a feature-flagged pin projection implementation using the pin-project
library?
Right now, I'm running into an issue where I'd like to deseralize a type like this:
#[derive(Deserialize)]
struct Foo<'a> {
#[serde(borrow)]
bar: Either<Cow<'a, str>, Baz<'a>>,
}
#[derive(Deserialize)]
struct Baz<'a> {
#[serde(borrow)]
xyz: Cow<'a, str>,
}
On older versions of Rust/Either/Serde, you'll get an error message like so:
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Deserialize` is not general enough
|
= note: `Foo<'a>` must implement `Deserialize<'0>`, for any lifetime `'0`...
= note: ...but `Foo<'_>` actually implements `Deserialize<'1>`, for some specific lifetime `'1`
Although running the same code on all updated versions of software just gives you a much longer error.
error[E0277]: the trait bound `Either<Cow<'a, str>, Baz<'a>>: Deserialize<'_>` is not satisfied
--> src/lib.rs:10:10
|
10 | bar: Either<Cow<'a, str>, Baz<'a>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `Either<Cow<'a, str>, Baz<'a>>`
|
= help: the following other types implement trait `Deserialize<'de>`:
bool
char
isize
i8
i16
i32
i64
i128
and 137 others
note: required by a bound in `next_element`
--> /playground/.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.192/src/de/mod.rs:1726:12
|
1724 | fn next_element<T>(&mut self) -> Result<Option<T>, Self::Error>
| ------------ required by a bound in this associated function
1725 | where
1726 | T: Deserialize<'de>,
| ^^^^^^^^^^^^^^^^ required by this bound in `SeqAccess::next_element`
This should be possible, given the fact that I can replace Either<L, R>
with Result<Ok, Err>
like so, and it'll actually compile, and act properly in software.
#[derive(Deserialize)]
struct Foo<'a> {
#[serde(borrow)]
bar: Result<Cow<'a, str>, Baz<'a>>,
}
Not entirely sure what the deal is, but I believe if Result
can do it, then Either
should be able to as well? I'm not sure if there are any technical details getting in the way of implementing this, but there shouldn't be much stopping us from just duplicating the Result
implementation here.
I suggest adding the two impls:
impl From<L> for Either<L, R>
and
impl From<R> for Either<L, R>
(don't know the right syntax by heart)
This enables the following nice use-case:
fn foo(e: Either<i32, f32>) {
// use e
}
fn main() {
foo(32.into());
foo(42.0.into());
}
Instead of explicitly writing out the type each time.
Is there a problem with this/am I missing something? Otherwise I would write up a PR. Thanks!
std::error::Error
has - for a while - now functions like source
and backtrace
which are currently not implemented in our Error
impl.
I am happy to send a PR for this.
Following warning is shown while using either
@1.5.3
is used as a dependency:
warning: trait objects without an explicit `dyn` are deprecated
--> /home/trivikr/.cargo/registry/src/github.com-1ecc6299db9ec823/either-1.5.3/src/lib.rs:839:32
|
839 | fn cause(&self) -> Option<&Error> {
| ^^^^^ help: use `dyn`: `dyn Error`
|
= note: #[warn(bare_trait_objects)] on by default
The code which needs to be updated:
https://github.com/bluss/either/blob/cd15a341118b5af765450dcb6380c6dee17ed0f2/src/lib.rs#L839-L841
To replace ok_or(Err)
.
Either is on the playground, and has a serde
feature which can be quite useful to use, test, and play around with, but it's not enabled by default.
The playground allows defining some metadata, especially related to features: rust-lang/rust-playground#192 (comment)
Maybe it'd be possible to add that to Cargo.toml
to enable a more complete set of features when on playground?
The macro can be useful outside of this crate if one wants to deal with both variants in the same way but without them having the same type. For example:
trait Foo {
type T;
fn foo(self) -> Bar<Self::T>;
}
struct Bar<T> {
inner: T,
}
impl<L, R, LT, RT> Foo for Either<L, R>
where
L: Foo<T = LT>,
R: Foo<T = RT>,
{
type T = Either<LT, RT>;
fn foo(self) -> Bar<Self::T> {
let either_bar = either!(self, inner => inner.foo().inner);
Bar { inner: either_bar }
}
}
https://github.com/bluss/either/blob/1bd70a2e6e3ede6f546dc3d7cfe8e89edc91b7ab/src/lib.rs#L822-L830
The underlying iterator implementations may provide faster implementations for rfold
and nth_back
.
These should be favored, like we do in the Iterator
implementation.
Hello. I've not found a way to update Left(prev_value)
to Right(new_value)
in case when I need the previous value to create the new one. In Option
we have the method take
that replaces an old value with None
, and returns the old one to us. It would be nice to have something like this:
pub fn morph(&mut self, f: F, g: G) -> Either<L, R>
where
F: FnOnce(L) -> R,
G: FnOnce(R) -> L
Now to do this, I have to use Option
wrapping Either
to be able to have the third None
state.
struct Data {
member: Option<Either<L, R>>
}
impl Data {
fn update_member(&mut self) {
self.member = self.member.take().map(
|member| member.map_either(
|old_value| create_new_value(old_value),
|old_value| create_new_value(old_value)
).flip()
);
}
}
But actually I don't need it, and it makes me write more code for checking the option's state later. I just want to have a member with two correct states: L
and R
.
Thanks
Hi, not sure if there's a good way to handle this, but I wanted to use Option like I usually do in serde where if the field does not exist it is None, however this is not how it works here:
use serde::{Deserialize};
use either::Either;
#[derive(Deserialize, Debug)]
struct Test {
#[serde(with = "either::serde_untagged_optional")]
field: Option<Either<String, Vec<String>>>,
another_field: u32,
}
fn main() {
// deserialization
let data: Test = serde_json::from_str(
r#"{"another_field": 6}"#
).unwrap();
println!("found {:?}", data);
}
This panics because field is not found.
I've worked around this issue by just having a container struct for that particular field and just use either::serde_untagged but was just curious if this was intended behavior or not.
#[derive(Deserialize, Debug)]
struct Container(#[serde(with = "either::serde_untagged")] Either<String, Vec<String>>);
#[derive(Deserialize, Debug)]
struct Test {
field: Option<Container>,
another_field: u32,
}
I am providing a reprex for the use of either::serde_untagged.
I have not been able to use this feature, because
this line:
#[serde(with = "either::serde_untagged")]
fails to compile, and I cannot figure out why.
//main.rs
use serde::{Serialize, Deserialize};
use either::Either;
fn main() {
#[derive(Serialize, Deserialize, Debug)]
#[serde(transparent)]
struct IntOrString {
#[serde(with = "either::serde_untagged")]
inner: Either<Vec<String>, u8>
};
}
//Cargo Toml deps
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
either = "1.6.1"
I am grateful for any help
Currently, the implementation of Future
for Either
requires that both Future
s resolve to the same type:
Lines 1127 to 1141 in af9f5fb
This limits, which kinds of Future
s can be expressed using Either
. It would be more useful to set the Output
type to Either<A::Output, B::Output>
. This is an API breaking change.
The original behaviour can be restored using the Either::into_inner
function. In case both Future
s, A
and B
have the same Output
, the into_inner
function can be used to "unwrap" the Either
:
Lines 916 to 930 in af9f5fb
I think it might be useful to implement our (object-safe) trait for either easily with some attribute macro
There's some guidance that suggests std
is a more idiomatic feature name than use_std
: https://rust-lang-nursery.github.io/api-guidelines/naming.html#feature-names-are-free-of-placeholder-words-c-feature
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.