#枚举 #变体 #差异 #字段 #访问器 #属性 #diff

diff-enum

一个宏库,帮助通过差异定义枚举变体

5个版本

0.1.4 2019年3月28日
0.1.3 2019年2月6日
0.1.2 2019年2月1日
0.1.1 2019年1月31日
0.1.0 2019年1月30日

#21#accessor

每月22次下载
用于 detect_git_service

MIT 许可证

11KB
96

#[diff_enum] Rust宏

crates.io documentation CI Status

这是一个小型Rust库,提供了一个属性宏 #[diff_enum::common_fields],通过差异帮助定义 enum 变体。当需要处理几乎相同但部分不同的数据时非常有用。

通过属性宏,可以分别定义所有变体之间的公共字段和每个变体的不同字段。公共字段只定义一次。此外,还会自动定义公共字段的访问器方法。

例如,

extern crate diff_enum;
use diff_enum::common_fields;

#[common_fields {
    user: String,
    name: String,
    stars: u32,
    issues: u32,
}]
#[derive(Debug)]
enum RemoteRepo {
    GitHub {
        language: String,
        pull_requests: u32,
    }
    GitLab {
        merge_requests: u32,
    }
}

被展开为

#[derive(Debug)]
enum RemoteRepo {
    GitHub {
        language: String,
        pull_requests: u32,
        user: String,
        name: String,
        stars: u32,
        issues: u32,
    }
    GitLab {
        merge_requests: u32,
        user: String,
        name: String,
        stars: u32,
        issues: u32,
    }
}

此外,还会为公共字段定义访问器函数。例如,

let repo = RemoteRepo::GitHub {
    user: "rust-lang".to_string(),
    name: "rust".to_string(),
    language: "rust".to_string(),
    issues: 4536,
    pull_requests: 129,
    stars: 33679,
};

println!("User: {}", repo.user());

替代方案

如果没有这个crate,通常会将数据分为具有公共字段的结构体和表示差异的枚举变体。

对于上面的 RemoteRepo 例子,

enum RemoteRepoKind {
    GitHub {
        language: String,
        pull_requests: u32,
    }
    GitLab {
        merge_requests: u32,
    }
}
struct RemoteRepo {
    user: String,
    name: String,
    stars: u32,
    issues: u32,
    kind: RemoteRepoKind,
}

这个解决方案存在以下问题

  • 字段被分成两部分,这是由于Rust枚举的原因。本质上,GitHub仓库的issue数量和pull request数量都是属性。作为自然的数据结构,它们应该位于相同的扁平结构体中。
  • 为内部枚举命名很困难。在这里我使用了'Kind'来区分部分。但这是否合适?'Kind'是一个过于通用的名称,含义较弱。弱名称来自数据结构的尴尬。

安装

请将此crate添加到项目的Cargo.toml依赖中。

[dependencies]
diff-enum = "0.1"

用法

首先,请加载此crate。

extern crate diff_enum;
use diff_enum::common_fields;

并使用#[common_fields]属性宏为您的枚举定义。

#[common_fields {
    common fields here...
}]
enum ...

或者如果您喜欢,可以使用完全限定名称

#[diff_enum::common_fields {
    common fields here...
}]
enum ...

任何属性和注释都可以像普通的enum字段一样放置到公共字段中。

定义了与公共字段相对应的访问器方法。它是一个非常有用的辅助工具,可以在不使用模式匹配的情况下访问公共字段。

例如,

#[common_fields { i: i32 }]
enum E { A, B{ b: bool } }

如下生成对i的访问器方法

impl E {
    fn i(&self) -> &i32 {
        match self {
            E::A{ref i, ..} => i,
            E::B{ref i, ..} => i,
        }
    }
}

在以下情况下,属性宏会导致编译错误。

  • 当没有放置公共字段时
  • 当属性参数中的字段不是field: type的形式时
  • #[common_fields {...}]设置为除了enum定义之外的内容时
  • 当在enum定义中使用元组样式的枚举变体时

许可证

MIT许可证下分发。

依赖关系

约2MB
约46K SLoC