2 个版本
0.1.1 | 2021年6月13日 |
---|---|
0.1.0 | 2021年6月11日 |
#565 在 进程宏
每月 773 次下载
26KB
212 行
variants-struct
一个 derive 宏,将枚举转换为具有成员的结构。实际上,它就像使用一个 HashMap<MyEnum, MyData>
,但生成的是硬编码的结构而不是 HashMap,以减少开销。
基本示例
将宏应用于基本枚举(即不带元组变体或结构变体的枚举)如下
use variants_struct::VariantsStruct;
#[derive(VariantsStruct)]
enum Hello {
World,
There
}
将生成以下代码
struct HelloStruct<T> {
pub world: T,
pub there: T
}
impl<T> HelloStruct<T> {
pub fn new(world: T, there: T) -> HelloStruct<T> {
HelloStruct {
world,
there
}
}
pub fn get_unchecked(&self, var: &Hello) -> &T {
match var {
&Hello::World => &self.world,
&Hello::There => &self.there
}
}
pub fn get_mut_unchecked(&mut self, var: &Hello) -> &mut T {
match var {
&Hello::World => &mut self.world,
&Hello::There => &mut self.there
}
}
pub fn get(&self, var: &Hello) -> Option<&T> {
match var {
&Hello::World => Some(&self.world),
&Hello::There => Some(&self.there)
}
}
pub fn get_mut(&mut self, var: &Hello) -> Option<&mut T> {
match var {
&Hello::World => Some(&mut self.world),
&Hello::There => Some(&mut self.there)
}
}
}
成员可以直接访问(如 hello.world
)或使用获取方法,如
let mut hello = HelloStruct::new(2, 3);
*hello.get_mut_unchecked(&Hello::World) = 5;
assert_eq!(hello.world, 5);
assert_eq!(hello.world, *hello.get_unchecked(&Hello::World));
获取器与 enum-iterator 包特别有用。对于基本枚举,checked-getters 总是返回 Some(...)
,因此建议使用 get_unchecked
,但枚举包含元组变体时则不是这样。
请注意,枚举变体从驼峰式命名改为蛇形命名,以符合Rust的命名约定。
可见性
结构字段始终是 pub
,并且结构具有与枚举相同的可见性。
自定义结构
重命名
默认情况下,结构的名称是 <OriginalEnumName>Struct
。您可以使用 struct_name
属性将其设置为其他名称。例如,这
#[derive(VariantsStruct)]
#[struct_name = "SomeOtherName"]
enum NotThisName {
Variant
}
将生成一个名为 SomeOtherName
的结构。
您还可以使用 field_name
属性手动重命名单个字段。例如,这
#[derive(VariantsStruct)]
enum ChangeMyVariantName {
#[field_name = "this_name"] NotThisName
}
将生成以下结构
struct ChangeMyVariantName<T> {
this_name: T
}
派生
默认情况下,不向生成的结构应用派生。您可以使用 struct_derive
属性添加 derive 宏调用。例如,这
use serde::{Serialize, Deserialize};
#[derive(VariantsStruct)]
#[struct_derive(Debug, Default, Serialize, Deserialize)]
enum Hello {
World,
There
}
将生成以下代码
#[derive(Debug, Default, Serialize, Deserialize)]
struct HelloStruct<T> {
pub world: T,
pub there: T
}
// impl block omitted
特质界限
默认情况下,结构的类型参数 T
没有特质界限,但您可以使用 struct_bounds
属性添加它们。例如,这
#[derive(VariantsStruct)]
#[struct_bounds(Clone)]
enum Hello {
World,
There
}
将生成以下代码
struct HelloStruct<T: Clone> {
# go_away: T,
// fields omitted
}
impl<T: Clone> HelloStruct<T> {
// methods omitted
}
组合
请注意,许多派生并不要求类型参数 T
满足任何特质约束。例如,将 Clone
派生应用于结构体仅使结构体在 T
可克隆时才可克隆,同时仍然允许与结构体一起使用不可克隆的类型。
因此,如果您想使结构体始终可克隆,您必须同时使用派生和特质约束
#[derive(VariantsStruct)]
#[struct_derive(Clone)]
#[struct_bounds(Clone)]
enum Hello {
// variants omitted
}
这两个属性以及 struct_name
属性可以按任何顺序使用,甚至可以多次使用(尽管这不会非常易读)。
元组和结构体变体
元组变体会转换为 HashMap
,其中元组中存储的数据是键(因此数据必须实现 Hash
)。不幸的是,包含多个字段的变体不支持。
元组变体被省略在结构体的 new
函数中。例如,这
#[derive(VariantsStruct)]
enum Hello {
World,
There(i32)
}
产生以下代码
struct HelloStruct<T> {
pub world: T,
pub there: std::collections::HashMap<i32, T>
}
impl<T> HelloStruct<T> {
fn new(world: T) -> HelloStruct<T> {
HelloStruct {
world,
there: std::collections::HashMap::new()
}
}
pub fn get_unchecked(&self, var: &Hello) -> &T {
match var {
&Hello::World => &self.world,
&Hello::There(key) => self.there.get(&key)
.expect("tuple variant key not found in hashmap")
}
}
pub fn get_mut_unchecked(&mut self, var: &Hello) -> &mut T {
match var {
&Hello::World => &mut self.world,
&Hello::There(key) => self.there.get_mut(&key)
.expect("tuple variant key not found in hashmap")
}
}
pub fn get(&self, var: &Hello) -> Option<&T> {
match var {
&Hello::World => Some(&self.world),
&Hello::There(key) => self.there.get(&key)
}
}
pub fn get_mut(&mut self, var: &Hello) -> Option<&mut T> {
match var {
&Hello::World => Some(&mut self.world),
&Hello::There(key) => self.there.get_mut(&key)
}
}
}
请注意,现在的 new
函数现在只接受 world
参数,未检查的getter方法查询哈希表并解包结果。
这也可以在只有一个字段的变体结构体中完成。
许可证:MIT OR Apache-2.0
依赖项
~1.5MB
~39K SLoC