1 个不稳定版本

0.1.6 2023年3月4日

#1330过程宏

MIT/Apache

18KB
321 代码行

penum

Github Download crates.io

penum 是一个过程宏,用于使枚举符合给定的模式,该模式可以包括具有特例约束的泛型。

安装

此crate在 crates.io 上可用,可以通过将以下内容添加到您的项目的 Cargo.toml 中来使用:

[dependencies]
penum = "0.1.6"

或者在您的cargo项目中运行以下命令:

$ cargo add penum

概述

一个 pattern 由一个或多个 shapes 和一个可选的 where 子句 组成,它会自动绑定与您的模式匹配的所有具体类型--带有您指定的特例约束。

  • 一个 shape 可以是 NamedUnnamedUnit,用于验证变体。
  • 一个 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