30个版本
0.1.29 | 2023年7月22日 |
---|---|
0.1.28 | 2023年7月22日 |
0.1.27 | 2023年6月3日 |
0.1.26 | 2023年4月18日 |
0.1.20 | 2023年3月31日 |
#174 在 过程宏 中
111 每月下载量
640KB
3K SLoC
penum
是一个用于 枚举一致性 和 静态分发 的过程宏。这是通过指定一个声明式模式来完成的,该模式表达了我们应该如何解释枚举。它是一个通过简单的Rust语法断言枚举应该 看起来 和 行为 的工具。
-
模式 可以被认为是一个 玩具形状分类器,它通过枚举变体进行分类,并确保它们符合。所以每个变体都有一个必须满足我们指定的模式的特定形状。有三种形状可以选择,分别是 元组
()
,结构体{}
和 单位。 -
谓词 与 模式 结合使用,用于断言匹配的变体的字段类型应实现什么。它们可以像正常的where子句一样表达,例如
where T: Trait<Type>
。需要在模式片段内引入 泛型参数。 -
智能分发 允许我们表达枚举相对于其变体的行为。用于表达这个的符号是
^
,并且应该放在你想分发的特前面。
安装
此crate可在 crates.io 上找到,可以通过将以下内容添加到项目的Cargo.toml中使用:
[dependencies]
penum = "0.1.29"
或在您的cargo项目中运行以下命令:
$ cargo add penum
最新特性
现在您可以将枚举 discriminants
作为表达式块用于 ToString
、Display
、Into<T>
和 Deref<Target = T>
。这可以作为 const 声明的一种替代,并且还可以避免内部属性。
#[penum::to_string]
enum EnumVariants {
Variant0 = "Return on match",
Variant1(i32) = "Return {f0} on match",
Variant2(i32, u32) = stringify!(f0, f1),
Variant3 { name: String } = format!("My string {name}"),
Variant4 { age: u32 } = age.to_string(),
Variant5 = EnumVariants::Variant0.to_string(),
Variant6 { list: Vec<u32> } = {
let string = list
.iter()
.map(ToString::to_string)
.join(", ");
format!("List: ({string})")
},
Variant7,
Variant8,
// Note that default will not appear in the Enum, i.e `EnumVariants::default` will not exist.
// Also, we might change this in the future, e.g. using `fallback` instead?
default = "Variant7 and Variant8 will return this default"
}
let enum_variants = Enum::Variant0;
println!("{}", enum_variants.to_string());
将以下之一添加到您的枚举中,以启用枚举判别表达式。
-
penum::to_string
— 当您只想实现ToString
时很有用。 -
penum::fmt
— 当您想实现ToString
和Display
时很有用。 -
penum::into(T)
— 当您想转换您的变体Into<T>
时很有用。 -
penum::deref(T)
— 当您想利用 Rust 自动解引用功能时很有用。 -
penum::static_str
— 将实现Deref<Str>
和AsRef<str>
,包括一些辅助方法,如:.as_str()
和.static_str()
。
如果您喜欢这个 feature
,请务必尝试使用 penum::penum
。请注意,penum::penum
不可与 penum::penum
兼容,并且应单独使用,因为它们是互斥的。
请注意,penum::penum
可能会被改为 penum::expr
、penum::declare
或 pemum::express
。
概述
Penum
表达式可以看起来像这样
Dispatch symbol.
|
#[penum( (T) where T: ^Trait )]
^^^ ^^^^^^^^^
| |
| Predicate bound.
|
Pattern fragment.
未指定模式的 Penum
表达式
#[penum( impl Trait for Type )]
^^^^^^^^^^^^^^^^^^^
_ where Type: ^Trait
的简写语法
对于您想要分发的特性,重要的是要包含 ^
。
#[penum( impl Type: ^Trait )]
请注意,在表达式 penum 实现中不需要 ^
。
#[penum( impl Trait for Type )]
在 Rust 1.68.0 中,对于 {f32,f64}
的 From<bool>
已稳定。这意味着您可以这样做。
#[penum( impl From<bool> for {f32,f64} )]
简单示例
使用 Penum
自动 实现
枚举的特质。
#[penum(impl String: ^AsRef<str>)]
enum Store {
V0(),
V1(i32),
V2(String, i32),
V3(i32, usize, String),
V4(i32, String, usize),
V5 { age: usize, name: String },
V6,
}
- 将变为这样
impl AsRef<str> for Store {
fn as_ref(&self) -> &str {
match self {
Store::V2(val, ..) => val.as_ref(),
Store::V3(_, _, val) => val.as_ref(),
Store::V4(_, val, ..) => val.as_ref(),
Store::V5 { name, .. } => name.as_ref(),
_ => "",
}
}
}
也支持用户自定义特质,但请确保它们在枚举之前被标记。
#[penum]
trait Trait {
fn method(&self, text: &str) -> &Option<&str>;
}
支持的 std 特质
Any
、Borrow
、BorrowMut
、Eq
、AsMut
、AsRef
、From
、Into
、TryFrom
、TryInto
、Default
、Binary
、Debug
、Display
、LowerExp
、LowerHex
、Octal
、Pointer
、UpperExp
、UpperHex
、Future
、IntoFuture
、FromIterator
、FusedIterator
、IntoIterator
、Product
、Sum
、Sized
、ToSocketAddrs
、Add
、AddAssign
、BitAnd
、BitAndAssign
、BitOr
、BitOrAssign
、BitXor
、BitXorAssign
、Deref
、DerefMut
、Div
、DivAssign
、Drop
、Index
、IndexMut
、Mul
、MulAssign
、MultiMethod
、Neg
、Not
、Rem
、RemAssign
、Shl
、ShlAssign
、Shr
、ShrAssign
、Sub
、SubAssign
、Termination
、SliceIndex
、FromStr
、ToString
Penum
足够智能,可以推断非匹配变体的某些返回类型。例如 Option<T>
、&Option<T>
、String
、&str
。它甚至可以处理 &String
、引用非常量类型。目标是支持任何类型,我们可能通过检查实现 Default
特质的类型来实现。
注意,在分派具有关联类型的特质时,声明它们很重要。例如 Add<i32, Output = i32>
。
示例
使用 penum 强制每个变体都成为具有一个字段(该字段必须实现 Trait
)的元组。
#[penum( (T, ..) where T: Trait )]
enum Guard {
Bar(String),
^^^^^^
// ERROR: `String` doesn't implement `Trait`
Bor(Option<String>),
^^^^^^^^^^^^^^
// ERROR: `Option<String>` doesn't implement `Trait`
Bur(Vec<String>),
^^^^^^^^^^^
// ERROR: `Vec<String>` doesn't implement `Trait`
Byr(),
^^^^^
// ERROR: `Byr()` doesn't match pattern `(T)`
Bxr { name: usize },
^^^^^^^^^^^^^^^
// ERROR: `{ nname: usize }` doesn't match pattern `(T)`
Brr,
^^^
// ERROR: `Brr` doesn't match pattern `(T)`
Bir(i32, String), // Works!
Beer(i32) // Works!
}
如果你不关心实际的模式匹配,则可以使用 _
自动推断每个形状和字段。结合具体的分派类型,你就得到了一个自动分派器。
开发中
对于非标准类型,我们依赖于Default
特性,这意味着,如果我们能证明一个类型实现了Default
,我们可以自动将其添加为非匹配变体的返回类型。
#[penum( _ where Ce: ^Special, Be: ^AsInner<i32> )]
enum Foo {
V1(Al),
V2(i32, Be),
V3(Ce),
V4 { name: String, age: Be },
}
// Will create these implementations
impl Special for Foo {
fn ret(&self) -> Option<&String> {
match self {
Foo::V3(val) => val.ret(),
_ => None,
}
}
}
impl AsInner<i32> for Foo {
fn as_inner(&self) -> &i32 {
match self {
Foo::V2(_, val) => val.as_inner(),
Foo::V4 { age, .. } => age.as_inner(),
_ => &0,
}
}
}
- 它与这个相同
#[penum(impl Ce: ^Special, Be: ^AsInner<i32>)]
更多详情
-
Impls —— 可以被视为实现此特性的具体类型的缩写,主要用作常规泛型特性边界的替代。它们看起来像这样,
(impl Copy, impl Copy) | {name: impl Clone}
-
占位符 —— 是单个无界通配符,如果你熟悉Rust,那就是下划线标识符
_
,通常表示忽略某个东西,这意味着它们将满足任何类型(_, _) | {num: _}
。 -
可变参数 —— 与占位符类似,但它们不仅能替换一个类型,还可以替换0个或多个类型。与占位符一样,它们是一种表达我们不在乎模式中其余参数的方式。它们看起来像这样
(T, U, ..) | {num: T, ..}
。
可能有用的未来想法
尚未支持 - 进行中
实际上,大多数时候,你很可能想按常规方式实现事物,但是当你计划一个非常小的实现时,这可能就足够了。
#[penum]
enum Enum {
Variant0(String) = implement! {
ToString => "My incoming string: {f0}",
Deref[Target = str] => &**f0,
AsRef[str] => f0,
},
default = {
ToString => "My custom fallback string",
_ => Default::default()
}
}
#[penum]
enum Enum {
Variant0(String) = implement! {
ToString => "My incoming string: {f0}",
},
Variant1(&'static str, i32) = implement! {
ToString => "My incoming string: {f0}",
},
default = {
ToString => "My custom fallback string",
_ => Default::default()
}
}
#[penum]
enum Enum {
Variant0(String) = implement! {
ToString {
format!("My incoming string: {f0}")
},
Deref<Target = str> {
&**f0
},
AsRef<str> { f0 },
},
default = implement! {
ToString { "My custom fallback string" },
_ { Default::default() }
}
}
#[penum]
enum Enum {
Variant0(String) = implement! {
ToString => {
format!("My incoming string: {f0}")
},
Deref<Target = str> => {
&**f0
},
AsRef<str> => { f0 },
},
default = implement! {
ToString { "My custom fallback string" },
_ { Default::default() }
}
}
#[penum]
enum Enum {
Variant0(String) = implement! {
ToString => {
format!("My incoming string: {f0}")
},
Deref<Target = str> => {
&**f0
},
AsRef<str> => { f0 },
},
default = implement! {
ToString => { "My custom fallback string" },
_ => { Default::default() }
}
}
依赖项
~21–33MB
~601K SLoC