1 个不稳定版本
0.1.6 | 2023年3月4日 |
---|
#1330 在 过程宏
18KB
321 代码行
penum
penum
是一个过程宏,用于使枚举符合给定的模式,该模式可以包括具有特例约束的泛型。
安装
此crate在 crates.io 上可用,可以通过将以下内容添加到您的项目的 Cargo.toml 中来使用:
[dependencies]
penum = "0.1.6"
或者在您的cargo项目中运行以下命令:
$ cargo add penum
概述
一个 pattern
由一个或多个 shapes
和一个可选的 where 子句
组成,它会自动绑定与您的模式匹配的所有具体类型--带有您指定的特例约束。
- 一个
shape
可以是Named
、Unnamed
或Unit
,用于验证变体。 - 一个
where
子句用于将泛型参数绑定到特例。 - 泛型参数 只能 使用大写字母或下划线声明。例如
(T, FOO, BAR)
是有效的泛型参数,但(t, Foo, BaR)
不是,它们被认为是 具体类型。
未来计划
静态调度
- 将自动实现常见的std
特例。Spread/range 操作符
- 将允许变长字段(T, U, ..) | {num: T, ..}
判别式
- 可能支持#ident(T) = func(#ident)
,或类似的东西。
用法
通常,在枚举中使用泛型意味着它将应用于整个枚举,而不是每个变体。例如,如果我想指定所有变体都应该是一个 tuple(T)
,其中 T 必须实现 Copy
,我必须为所有变体指定泛型
enum Foo where T: Copy, U: Copy, F: Copy {
Bar(T),
Ber(U),
Bur(F)
// But if I now want to add `Bor(D)` to this
// enum, I'd have to add it manually, and then
// bind that generic to impl copy.
// Also, there is nothing stopping me from
// changing the variant shape to `Bor(D, i32)`.
}
这看起来有点繁琐,因为我们只是想让枚举符合特定的模式,就像这样
// This forces all current and future variants to
// contain one field which must implement `Copy`.
#[penum( (T) where T: Copy )]
enum Foo {
Bar(i32),
Ber(u32),
Bur(f32)
}
..这将扩展到上面的第一个示例。
示例
也可以通过使用 |
符号分隔 shape
来使枚举符合多个形状,例如
#[penum( (T) | (T, T) | { num: T } where T: Copy )]
enum Foo {
Bar(i32),
Ber(u32, i32),
Bur { num: f32 }
}
此外,如果枚举应该破坏 pattern
,例如,如果一个变体没有实现正确的 Trait
,将发生错误
#[penum( (T) | (T, T) | { num: T } where T: Copy )]
enum Foo {
Bar(String),
^^^^^^
// ERROR: `String` doesn't implement `Copy`
Ber(u32, i32),
Bur { num: f32 }
}
..或者如果变体与指定的 shape
不匹配
#[penum( (T) | (T, T) | { num: T } where T: Copy )]
enum Foo {
Bar(u32),
Ber(u32, i32, i32),
^^^^^^^^^^^^^
// Found: `Ber(u32, i32, i32)`
// Expected: `(T) | (T, T) | { num: T }`
Bur { num: f32 }
}
有时我们不在乎指定 where 子句
,只希望我们的枚举遵循特定的 shape
。这是通过指定 _
来完成的
#[penum( (_) | (_, _) | { num: _ } )]
enum Foo {
Bar(u32),
Ber(u32, i32, i32),
Bur { num: f32 }
}
演示
use penum::shape;
trait Trait {}
impl Trait for f32 {}
impl Trait for i32 {}
trait Advanced {}
impl Advanced for usize {}
#[penum( (T, T, U) | (T, U) | { name: T } where T: Trait, U: Advanced )]
enum Vector3 {
Integer(i32, f32, usize),
Float(f32, i32, usize),
}
#[penum( { name: _, age: usize } where usize: Advanced )]
enum Strategy<'a> {
V1 { name: String, age: usize },
V2 { name: usize, age: usize },
V3 { name: &'a str, age: usize },
}
#[penum( { name: &'a str, age: usize } )]
enum Concrete<'a> {
Static { name: &'a str, age: usize },
}
#[penum( tuple(_) )]
enum Must<'a> {
Static { name: &'a str, age: usize }
^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Found: `Static { name : & 'a str, age : usize }`
// Expected: `tuple(_)`
}
// Note that this shape has a name (`tuple`). Right now
// it doesn't do anything,but there is an idea of using
// regexp to be able to validate on Variant names too.
// Also, there is thoughts about using these Idents to
// specify other rules, like if penum should auto implement
// a static dispatch for a certain pattern. But this could
// also be done by other rules.
#[penum( tuple(T) where T: Trait )]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
`the trait bound `usize: Trait` is not satisfied`
enum Must {
Static (usize)
}
依赖关系
~1.5MB
~35K SLoC