将 Rust 枚举转换为子枚举

pas*_*ugh 4 enums code-generation casting reusability rust

我正在创建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
}
// ???
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,现在我需要再次impl用 es 编写这两个 s for ,甚至可能 for - 以及任何枚举子集。如何避免这样的样板?matchStoreOrdering <-> LoadOrderingStoreOrdering <-> LoadOrdering

Geo*_*ion 5

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();
}
Run Code Online (Sandbox Code Playgroud)

游乐场链接

当然,还有很多可以改进的地方。但是,这应该可以解决您现在的问题。