Can I unpack a either::Either by the contained type, not by side?
Say I have a Either<u8, f32>
, and want to either get None
or a value with the desired type, without knowing with side the value is in.
I've tried doing it with macros like this:
/// Unpack a 2 option item
#[macro_export]
macro_rules! unpack_dual {
( $item: expr, $item_t: ty ) => {
match $item.inner {
either::Either<$item_t, _> => {
if let either::Right(item) => $item.inner {
Some(item)
} else {
None
}
}
},
either::Either<_, $item_t> => {
if let either::Left(item) = $item.inner {
Some(item)
} else {
None
}
}
};
}
where you pass the value and the desired type, but It doesn't work because you can't match on the Either
shape.
This is for ease of use with a configuration file parser where there are fields that may be one of 2 things it uses Either
.
Like in this example struct, using structstruck
for nesting and serde
for deserialization.
use std::collections::HashMap;
use either::Either;
extern crate structstruck;
use serde::{Deserialize};
use serde_yaml as yaml;
structstruck::strike! {
#[derive(Debug, Deserialize)]
struct Example {
pub dual_field: HashMap<String,
#[derive(Debug, Deserialize)]
#[serde(transparent)]
pub struct TypeAOrTypeB {
#[serde(with = "either::serde_untagged")]
pub inner:
Either<#[derive(Debug, Deserialize)]
pub struct TypeA {
// -- snip --
},
#[derive(Debug, Deserialize)]
pub struct TypeA {
// -- snip --
}>>
}
// -- snip ---
}
The best way I can come up with that uses only std
is via Any
which restricts this approach to types that are 'static
.
use either::Either::{self, *};
use std::any::Any;
fn unpack_dual<L: 'static, R: 'static, T: 'static>(e: &Either<L, R>) -> Option<&T> {
let e: Either<&dyn Any, &dyn Any> = e.as_ref().either(|l| Left(l as &dyn Any), |r| Right(r as &dyn Any));
e.either(|l| l.downcast_ref::<T>(), |r| r.downcast_ref::<T>())
}
fn main() {
let e: Either<u8, f32> = Right(3.1415);
let maybe_u8: Option<&u8> = unpack_dual(&e);
let maybe_f32: Option<&f32> = unpack_dual(&e);
let probably_not_string: Option<&String> = unpack_dual(&e);
assert_eq!(maybe_u8, None);
assert_eq!(maybe_f32, Some(&3.1415));
assert_eq!(probably_not_string, None);
}