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过程宏

Download history 8/week @ 2024-03-11 2/week @ 2024-03-18 12/week @ 2024-04-01 2/week @ 2024-05-20

111 每月下载量

MIT/Apache

640KB
3K SLoC

Github

penum 是一个用于 枚举一致性静态分发 的过程宏。这是通过指定一个声明式模式来完成的,该模式表达了我们应该如何解释枚举。它是一个通过简单的Rust语法断言枚举应该 看起来行为 的工具。

  • 模式 可以被认为是一个 玩具形状分类器,它通过枚举变体进行分类,并确保它们符合。所以每个变体都有一个必须满足我们指定的模式的特定形状。有三种形状可以选择,分别是 元组 ()结构体 {}单位

  • 谓词模式 结合使用,用于断言匹配的变体的字段类型应实现什么。它们可以像正常的where子句一样表达,例如 where T: Trait<Type>。需要在模式片段内引入 泛型参数

  • 智能分发 允许我们表达枚举相对于其变体的行为。用于表达这个的符号是 ^,并且应该放在你想分发的特前面。

安装

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

[dependencies]
penum = "0.1.29"

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

$ cargo add penum

最新特性

现在您可以将枚举 discriminants 作为表达式块用于 ToStringDisplayInto<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 — 当您想实现 ToStringDisplay 时很有用。

  • 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::exprpenum::declarepemum::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 特质

AnyBorrowBorrowMutEqAsMutAsRefFromIntoTryFromTryIntoDefaultBinaryDebugDisplayLowerExpLowerHexOctalPointerUpperExpUpperHexFutureIntoFutureFromIteratorFusedIteratorIntoIteratorProductSumSizedToSocketAddrsAddAddAssignBitAndBitAndAssignBitOrBitOrAssignBitXorBitXorAssignDerefDerefMutDivDivAssignDropIndexIndexMutMulMulAssignMultiMethodNegNotRemRemAssignShlShlAssignShrShrAssignSubSubAssignTerminationSliceIndexFromStrToString

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