either icon indicating copy to clipboard operation
either copied to clipboard

Add a `FromEither` trait

Open c3potheds opened this issue 1 year ago • 1 comments

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

c3potheds avatar May 05 '24 07:05 c3potheds

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.

cuviper avatar May 05 '24 23:05 cuviper