I'm creating subsets of std::sync::atomic::Ordering:
use std::sync::atomic::Ordering;
pub enum StoreOrdering {
Relaxed,
Release,
SeqCst
}
impl Into<Ordering> for StoreOrdering {
fn into(self) -> Ordering {
match self {
Self::Relaxed => Ordering::Relaxed,
Self::Release => Ordering::Release,
Self::SeqCst => Ordering::SeqCst
}
}
}
impl std::convert::TryFrom<Ordering> for StoreOrdering {
type Error = (); // HACK
fn try_from(ord: Ordering) -> Result<Self, Self::Error> {
match ord {
Ordering::Relaxed => Ok(Self::Relaxed),
Ordering::Release => Ok(Self::Release),
Ordering::SeqCst => Ok(Self::SeqCst),
_ => Err(())
}
}
}
enum LoadOrdering {
Acquire,
Relaxed,
SeqCst
}
// ???
As you can see, now I need to write those two impl
s with match
es again for StoreOrdering <-> LoadOrdering
and maybe even for StoreOrdering <-> LoadOrdering
- as well as for any enum subset. How to avoid such boilerplate?
Rust doesn't support duck typing like C++ does with templates. The only functionality that generics can access is determined by the trait bounds.
So any duck-type-like behaviour must be done with macros.
For this, you could use the given macro below. It only works for simple C-style macros. It creates the enum and auto-generates the conversions to the given super-enum.
use std::sync::atomic::Ordering;
use std::convert::TryInto;
// Create the store ordering
sub_enum!(StoreOrdering of Ordering {
Relaxed,
Release,
SeqCst
});
// Create the load ordering
sub_enum!(LoadOrdering of Ordering {
Acquire,
Relaxed,
SeqCst
});
#[macro_export]
macro_rules! sub_enum {
($sub_enum_name:ident of $super_enum_name:ty {
$($variant:ident),* $(,)?
}) => {
pub enum $sub_enum_name {
$($variant,)*
}
impl From<$sub_enum_name> for $super_enum_name {
fn from(val: $sub_enum_name) -> $super_enum_name {
match val {
$(<$sub_enum_name>::$variant => <$super_enum_name>::$variant,)*
}
}
}
impl std::convert::TryFrom<$super_enum_name> for $sub_enum_name {
type Error = ();
fn try_from(val: $super_enum_name) -> Result<Self, Self::Error> {
match val {
$(<$super_enum_name>::$variant => Ok(Self::$variant),)*
_ => Err(())
}
}
}
}
}
fn main() {
let store = StoreOrdering::SeqCst;
let general: Ordering = store.into();
let load: LoadOrdering = general.try_into().unwrap();
}
A lot could be improved still, of course. However, this should do for your problem right now.