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