#struct #enums #map

variants-struct

将枚举转换为具有成员的结构的 derive 宏

2 个版本

0.1.1 2021年6月13日
0.1.0 2021年6月11日

#565进程宏

Download history 48/week @ 2024-03-24 137/week @ 2024-03-31 7/week @ 2024-04-07 5/week @ 2024-04-14 29/week @ 2024-04-21 137/week @ 2024-04-28 192/week @ 2024-05-05 140/week @ 2024-05-12 228/week @ 2024-05-19 199/week @ 2024-05-26 136/week @ 2024-06-02 153/week @ 2024-06-09 228/week @ 2024-06-16 164/week @ 2024-06-23 221/week @ 2024-06-30 158/week @ 2024-07-07

每月 773 次下载

MIT/Apache

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