Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a FromEither trait #105

Open
c3potheds opened this issue May 5, 2024 · 1 comment
Open

Add a FromEither trait #105

c3potheds opened this issue May 5, 2024 · 1 comment

Comments

@c3potheds
Copy link

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).

@cuviper
Copy link
Member

cuviper commented May 5, 2024

I think either_into<T> broadly meets this need -- your example can write .map(Either::either_into::<C>), or drive the type inference for C elsewhere like .map(Either::either_into).for_each(|c: C| ...). It's a little wordy, perhaps, but at least you don't need to import an extra trait to make it work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants