penum
is a procedural macro that is used to make an enum conform to a
given pattern that can include generics with trait bounds, which then
allows for static dispatching
. It's a tool for asserting how enums
should look and behave through simple expressive rust grammar.
Patterns — can be though of a toy shape sorter, where the enum
variants are shape pieces that are trying to fit the given pattern
we've expressed. There are 3 shapes to choose from, tuples ()
,
structs {}
and units.
Trait bounds — are used in combination with generic parameters
to assert what the matched variants field types should implement, and
can be expressed like this where T: Trait<Type>
. The generic
parameters actually needs to be introduced inside a pattern fragment.
Static dispatch — lets us express how an enum should behave in
respect to its variants. The symbol that is used to express this is
^
and should be put infront of the trait you wish to be dispatched,
e.g. (T) where T: ^AsRef<str>
. This is currently limited to rust std
and core library traits, but there's plans to extend support for
custom trait definitions soon.
Impls — can be seen as a shorthand for a concrete type that
implements this trait, and are primarily used as a substitute for
regular generic trait bound expressions. They look something like
this, (impl Copy, impl Copy) | {name: impl Clone}
Placeholders — are single unbounded wildcards, or if you are
familiar with rust, it's the underscore _
identifier and usually
means that something is ignored, which means that they will satisfy
any type (_, _) | {num: _}
.
Variadic — are similar to placeholders, but instead of only being
able to substitute one type, variadics can be substituted by 0 or more
types. Like placeholders, they are a way to express that we don't care
about the rest of the parameters in a pattern. The look something like
this(T, U, ..) | {num: T, ..}
Allowing developers to assert how an enum should look and behave.
This crate is available on crates.io
and can be used by adding the following to your project's Cargo.toml:
toml
[dependencies]
penum = "0.1.15"
Or run this command in your cargo project:
sh
$ cargo add penum
A Penum
expression can look like this:
```text
^^^ ^^^^^^^^
| |
| Predicate bound.
|
Pattern fragment.
``` note that there can be multiple patterns fragments and predicate bounds.
Here's how it would look like if we wanted to dispatch a trait. ```text
|
Dispatch symbol
```
Here he have an enum with two unary tuple variants where the parameter
type Struct
implements the trait Trait
. The goal is to be able to
call the trait method
through Foo
. This can be accomplished
automatically marking the trait with a dispatch symbol ^
.
```rust
enum Foo { V1(Struct), V2(Struct), } ```
rust
impl Trait for Foo {
fn method(&self, text: &str) {
match self {
V1(val) => val.method(text),
V2(val) => val.method(text),
}
}
}
rust
struct Struct;
trait Trait {
fn method(&self, text: &str);
}
impl Trait for Struct {}
Used penum to force every variant to be a tuple with one field that must
implement Copy
.
```rust
enum Guard {
Bar(String),
^^^^^^
// ERROR: String
doesn't implement Copy
Bor(&str),
^^^^
// ERROR: `&str` doesn't implement `Copy`
Bur(Vec<i32>),
^^^^^^^^
// ERROR: `Vec<i32>` doesn't implement `Copy`
Bir(i32, i32),
^^^^^^^^^^
// ERROR: `(i32, i32)` doesn't match pattern `(T)`
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)`
Beer(i32) // Works!
} ```
```rust
enum Guard {
Bar(String),
^^^^^^
// ERROR: String
doesn't implement Copy
Bor(Vec<&str>),
^^^^^^^^^
// ERROR: `Vec<&str>` doesn't implement `Copy`
Bur(Vec<i32>),
^^^^^^^^
// ERROR: `Vec<i32>` doesn't implement `Copy`
Bir(i32, i32),
^^^^^^^^^^
// ERROR: `(i32, i32)` doesn't match pattern `(T)`
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)`
Beer(i32) // Works!
} ```
Static dispatch
- auto implement core
/std
/custom
traits (read
more).| Traits | Supported |
| ---------- | ------------- |
|Any
| supported |
|Borrow
| supported |
|BorrowMut
| supported |
|Eq
| supported |
|AsMut
| supported |
|AsRef
| supported |
|From
| supported |
|Into
| supported |
|TryFrom
| supported |
|TryInto
| supported |
|Default
| supported |
|Binary
| supported |
|Debug
| supported |
|Display
| supported |
|LowerExp
| supported |
|LowerHex
| supported |
|Octal
| supported |
|Pointer
| supported |
|UpperExp
| supported |
|UpperHex
| supported |
|Future
| supported |
|IntoFuture
| supported |
|FromIterator
| supported |
|FusedIterator
| supported |
|IntoIterator
| supported |
|Product
| supported |
|Sum
| supported |
|Copy
| supported |
|Sized
| supported |
|ToSocketAddrs
| supported |
|Add
| supported |
|AddAssign
| supported |
|BitAnd
| supported |
|BitAndAssign
| supported |
|BitOr
| supported |
|BitOrAssign
| supported |
|BitXor
| supported |
|BitXorAssign
| supported |
|Deref
| supported |
|DerefMut
| supported |
|Div
| supported |
|DivAssign
| supported |
|Drop
| supported |
|Fn
| supported |
|FnMut
| supported |
|FnOnce
| supported |
|Index
| supported |
|IndexMut
| supported |
|Mul
| supported |
|MulAssign
| supported |
|MultiMethod
| supported |
|Neg
| supported |
|Not
| supported |
|Rem
| supported |
|RemAssign
| supported |
|Shl
| supported |
|ShlAssign
| supported |
|Shr
| supported |
|ShrAssign
| supported |
|Sub
| supported |
|SubAssign
| supported |
|Termination
| supported |
|SliceIndex
| supported |
|FromStr
| supported |
|ToString
| supported |