18 个版本 (2 个稳定版)
1.1.0 | 2023 年 4 月 23 日 |
---|---|
1.0.0 | 2023 年 3 月 21 日 |
0.4.0 | 2022 年 10 月 17 日 |
0.3.4 | 2022 年 5 月 10 日 |
0.1.8 | 2022 年 2 月 21 日 |
#1397 in Rust 模式
5,267 每月下载次数
用于 7 个 crate (5 个直接使用)
26KB
363 行
enum-assoc
此 crate 定义了一些宏,允许您将常量或数据与枚举变体关联。
使用时,必须将 #[derive(Assoc)]
附加到枚举上。从那里,使用 func
属性来定义将为该枚举实现的功能签名。使用 assoc
属性来定义在调用该函数时每个变体将返回的常量。
正向关联
以下是一个示例
use enum_assoc::Assoc;
const WA: &'static str = "wa";
#[derive(Assoc)]
#[func(pub const fn foo(&self) -> u8)]
#[func(pub fn bar(&self) -> &'static str)]
#[func(pub fn maybe_foo(&self) -> Option<u8>)]
#[func(pub fn with_default(&self) -> u8 { 4 })]
enum TestEnum {
#[assoc(foo = 255)]
#[assoc(bar = "wow")]
Variant1,
#[assoc(foo = 1 + 7)]
#[assoc(bar = "wee")]
#[assoc(with_default = 2)]
Variant2,
#[assoc(foo = 0)]
#[assoc(bar = WA)]
#[assoc(maybe_foo = 18 + 2)]
Variant3
}
fn main() {
println!("Variant1 foo: {}", TestEnum::Variant1.foo());
println!("Variant2 foo: {}", TestEnum::Variant2.foo());
println!("Variant3 foo: {}", TestEnum::Variant3.foo());
println!("Variant1 bar: {}", TestEnum::Variant1.bar());
println!("Variant2 bar: {}", TestEnum::Variant2.bar());
println!("Variant3 bar: {}", TestEnum::Variant3.bar());
println!("Variant1 maybe_foo: {:?}", TestEnum::Variant1.maybe_foo());
println!("Variant2 maybe_foo: {:?}", TestEnum::Variant2.maybe_foo());
println!("Variant3 maybe_foo: {:?}", TestEnum::Variant3.maybe_foo());
println!("Variant1 with_default: {:?}", TestEnum::Variant1.with_default());
println!("Variant2 with_default: {:?}", TestEnum::Variant2.with_default());
println!("Variant3 with_default: {:?}", TestEnum::Variant3.with_default());
}
输出
Variant1 foo: 255
Variant2 foo: 8
Variant3 foo: 0
Variant1 bar: wow
Variant2 bar: wee
Variant3 bar: wa
Variant1 maybe_foo: None
Variant2 maybe_foo: None
Variant3 maybe_foo: Some(20)
Variant1 with_default: 4
Variant2 with_default: 2
Variant3 with_default: 4
请注意,返回 Option
类型函数的特殊功能:变体可以完全省略 assoc
属性以自动返回 None
,并且产生值的变体无需显式将其包装在 Some
中。
这个输出是什么意思?
每个 #[func(fn_signature)]
属性生成如下内容
impl Enum {
fn_signature {
match self {
// ... arms
}
}
}
并且每个 #[assoc(fn_name = association)]
属性为其关联函数生成如下臂
variant_name => association,
就是这样。您使用的 fn_signature
的详细信息以及您在 association
区域中放置的内容完全由您决定。
因此,虽然技术上不是此 crate 的原始意图,但您可以免费生成一些更有趣/更复杂的关联
use enum_assoc::Assoc;
#[derive(Assoc)]
#[func(pub fn foo(&self, param: u8) -> Option<u8>)]
#[func(pub fn bar(&self, param: &str) -> String)]
#[func(pub fn baz<T: std::fmt::Debug>(&self, param: T) -> Option<String>)]
enum TestEnum2 {
#[assoc(bar = String::new() + param)]
Variant1,
#[assoc(foo = 16 + param)]
#[assoc(bar = String::from("Hello") + param)]
Variant2,
#[assoc(bar = some_str_func(param))]
#[assoc(baz = format!("{:?}", param))]
Variant3
}
fn some_str_func(s: &str) -> String {
String::from("I was created in a function") + s
}
fn main() {
println!("Variant1 foo: {:?}", TestEnum2::Variant1.foo(0));
println!("Variant2 foo: {:?}", TestEnum2::Variant2.foo(22));
println!("Variant1 bar: {}", TestEnum2::Variant1.bar("string"));
println!("Variant2 bar: {}", TestEnum2::Variant2.bar(" World!"));
println!("Variant3 bar: {}", TestEnum2::Variant3.bar("!"));
println!("Variant3 baz: {:?}", TestEnum2::Variant3.baz(1));
}
输出
Variant1 foo: None
Variant2 foo: 34
Variant1 bar: string
Variant2 bar: Hello World!
Variant3 bar: I was created in a function!
Variant3 baz: Some("1")
在 assoc
属性中访问枚举字段
在关联属性中,通过在其名称前加下划线,可以访问枚举变体的字段值。对于元组,名称由下划线前缀和字段索引组成。
use thiserror::Error;
#[derive(Error, Debug, Assoc, Clone)]
#[func(pub const fn status(&self) -> u16)]
pub enum ServiceError {
#[error("failed to start or finish a transaction")]
#[assoc(status = 500)]
TransactionError { source: sea_orm::DbErr },
#[error(transparent)]
#[assoc(status = _source.status())]
RequestParsingError {
source: request_parser::RequestParsingError,
},
}
mod request_parser {
use super::*;
#[derive(Error, Debug, Assoc, Clone)]
#[func(pub const status(&self) -> u16)]
pub enum RequestParsingError{
#[error("provided input was too large")]
#[assoc(status = 500)]
OutOfMemory
#[error("the resource id did not have correct format")]
#[assoc(status = 400)]
InvalidResourceIdFormat
#[error("external validator service returned an error")]
#[assoc(status = _0.status())]
ExternalValidatorError(validator::Error)
}
}
pub mod validator{
//...
}
反向关联
这也可以生成反向关联(常量到枚举变体)。以下是一个示例。
use enum_assoc::Assoc;
#[derive(Assoc, Debug)]
#[func(pub fn foo(s: &str) -> Option<Self>)]
#[func(pub fn bar(u: u8) -> Self)]
#[func(pub fn baz(u1: u8, u2: u8) -> Self)]
enum TestEnum3
{
#[assoc(foo = "variant1")]
#[assoc(bar = _)]
Variant1,
#[assoc(bar = 2)]
#[assoc(foo = "variant2")]
#[assoc(baz = (3, 7))]
Variant2,
#[assoc(foo = "I'm variant 3!")]
#[assoc(foo = "variant3")]
#[assoc(baz = _)]
Variant3
}
fn main()
{
println!("TestEnum3 foo(\"variant1\"): {:?}", TestEnum3::foo("variant1"));
println!("TestEnum3 foo(\"variant3\"): {:?}", TestEnum3::foo("variant3"));
println!("TestEnum3 foo(\"I'm variant 3!\"): {:?}", TestEnum3::foo("I'm variant 3!"));
println!("TestEnum3 foo(\"I don't exist\"): {:?}", TestEnum3::foo("I don't exist"));
println!("TestEnum3 bar(2): {:?}", TestEnum3::bar(2));
println!("TestEnum3 bar(55): {:?}", TestEnum3::bar(55));
println!("TestEnum3 baz(3, 7): {:?}", TestEnum3::baz(3, 7));
println!("TestEnum3 baz(0, 0): {:?}", TestEnum3::baz(0, 0));
}
输出
TestEnum3 foo("variant1"): Some(Variant1)
TestEnum3 foo("variant3"): Some(Variant3)
TestEnum3 foo("I'm variant 3!"): Some(Variant3)
TestEnum3 foo("I don't exist"): None
TestEnum3 bar(2): Variant2
TestEnum3 bar(55): Variant1
TestEnum3 baz(3, 7): Variant2
TestEnum3 baz(0, 0): Variant3
反向关联与正向关联工作方式略有不同
- 反向关联不得包含一个
self
参数(没有self
参数是正向关联与反向关联的区别所在) - 它们必须返回
Self
或Option<Self>
- 与正向关联不同,单个枚举变体可以定义任何数量的相同函数的
assoc
属性。 - 与正向关联不同,
assoc
属性定义了一个模式而不是一个表达式。这是因为反向关联控制匹配臂的左侧而不是右侧。 - 生成的函数将匹配包含所有函数参数的元组。
- 匹配臂将按从上到下的顺序排列,只有一个例外:任何通配符模式
_
总是放在底部。 - 任何反向关联函数的通配符关联不能超过1个。超过1个将导致编译错误。
- 如果为返回
Option<Self>
的函数未定义通配符模式,则会自动插入_ => None
肢。
因此,为了简单反向关联生成有效的代码,必须满足以下3个条件之一
- 反向关联返回
Option<Self>
,或 - 为正好一个变体定义了通配符(
_
)模式,或 - 每个可能的值都映射到一个枚举变体
- 注意:对于返回多个参数的反向关联,可以使用通配符为特定参数指定(例如
(5, _)
)。此宏不会像处理通配符(_
)那样重新排序这些参数。匹配臂将放置在枚举属性列中它出现的确切位置。
目前,没有方法可以将反向关联映射到元组或类似于结构的变体。
这个输出是什么意思?
每个反向关联的 #[func(fn_signature)]
属性生成如下内容
impl Enum {
fn_signature {
match (param1, param2, etc) {
// ... arms
}
}
}
并且每个反向关联的 #[assoc(fn_name = pattern)]
属性为其关联的函数生成如下臂
pattern => variant_name,
依赖项
~1.5MB
~35K SLoC