我正在创建 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
}
// ???
如您所见,现在我需要再次为
impl
甚至 match
以及任何枚举子集编写这两个 StoreOrdering <-> LoadOrdering
和 StoreOrdering <-> LoadOrdering
。如何避免这样的样板?
Rust 不支持鸭子类型,就像 C++ 的模板一样。泛型可以访问的唯一功能是由特征边界决定的。
因此任何类似鸭子类型的行为都必须使用宏来完成。
为此,您可以使用下面给定的宏。 它仅适用于简单的 C 风格宏。它创建枚举并自动生成给定超级枚举的转换。
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();
}
当然,还有很多可以改进的地方。 不过,这应该可以解决您现在的问题。
有一个名为 subenum 的包,它有一个派生宏,用于自动创建
SubEnum
以及转换 From<SubEnum> for SuperEnum
和 TryFrom<SuperEnum> for SubEnum
。
这是板条箱自述文件中的示例:
use subenum::subenum;
#[subenum(Edible)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Plant {
#[subenum(Edible)]
Basil,
#[subenum(Edible)]
Tomato,
Manzanita,
Pine,
}
fn main() -> Result<(), EdibleConvertError> {
let plant = Plant::Tomato;
// We can convert between them.
let edible = Edible::try_from(plant)?;
let _plant2 = Plant::from(edible);
// We can compare them.
assert_eq!(plant, edible);
// We derive anything that's derived on the parent, such as clone.
let edible2 = edible.clone();
Ok(())
}