8个版本

0.3.5 2021年3月13日
0.3.4 2020年7月21日
0.3.2 2020年1月11日
0.3.1 2019年5月25日
0.1.0 2019年1月21日

#491 in 过程宏

24每月下载量

MIT许可证

19KB
151

tylift

crate documentation license

通过添加属性tylift简单地将枚举变体提升到类型级别。这对于类型级别编程非常有用。

重要提示:此库提供的机制与尚未完全实现的实验性功能const泛型/min const泛型几乎相同。有关更多信息,请参阅以下相应部分。

该属性将枚举变体提升为其自己的类型。枚举类型变为一个种类——类型的类型——通过一个特质来模拟,替换了原始类型声明。在Rust中,特质界限(:)的语法与类型注解的语法完美地对应。因此,代码片段B: Bool也可以读作“种类为Bool的类型参数B”。

表示种类的特质是密封的,这意味着没有人能够向种类添加新的类型。变体可以持有给定种类类型的(未命名的)字段。应用于项目本身及其变体的属性(特别是文档注释)将被保留。展开的代码在#![no_std]环境中工作。

目前,没有自动化的方法来具体化提升的变体(即将它们映射到它们的项级别对应物)。提升的枚举类型不能对种类进行泛型。

第一个示例

use tylift::tylift;
use std::marker::PhantomData;

#[tylift]
pub enum Mode {
    Safe,
    Fast,
}

pub struct Text<M: Mode> {
    content: String,
    _marker: PhantomData<M>,
}

impl<M: Mode> Text<M> {
    pub fn into_inner(self) -> String {
        self.content
    }
}

impl Text<Safe> {
    pub fn from(content: Vec<u8>) -> Option<Self> {
        Some(Self {
            content: String::from_utf8(content).ok()?,
            _marker: PhantomData,
        })
    }
}

impl Text<Fast> {
    pub unsafe fn from(content: Vec<u8>) -> Self {
        Self {
            content: unsafe { String::from_utf8_unchecked(content) },
            _marker: PhantomData,
        }
    }
}

fn main() {
    let safe = Text::<Safe>::from(vec![0x73, 0x61, 0x66, 0x65]);
    let fast = unsafe { Text::<Fast>::from(vec![0x66, 0x61, 0x73, 0x74]) };
    assert_eq!(safe.map(Text::into_inner), Some("safe".to_owned()));
    assert_eq!(fast.into_inner(), "fast".to_owned());
}

安装

将这些行添加到您的Cargo.toml

[dependencies]
tylift = "0.3.5"

目前尚未验证与较旧版本的rustc的兼容性。此crate的较旧版本(≤ 0.3.2)仅依赖于rustc 1.32的功能。因此,您可能想查看它们。

Cargo功能

功能标志 span_errors 通过利用标记的跨度信息,极大地改进了错误信息。它使用了实验性功能 proc_macro_diagnostic,因此需要使用夜间的 rustc

更多示例

宏展开前的代码

use tylift::tylift;

#[tylift]
pub enum Bool {
    False,
    True,
}

#[tylift]
pub(crate) enum Nat {
    Zero,
    Succ(Nat),
}

#[tylift]
enum BinaryTree {
    Leaf,
    Branch(BinaryTree, Nat, BinaryTree),
}

#[tylift(mod)] // put all 3 items into the module `Power`
pub enum Power {
    On,
    Off,
}

#[tylift(mod direction)] // put all 3 items into the module `direction`
pub(crate) enum Direction {
    /// Higher and higher!
    Up,
    /// Lower and lower...
    Down,
}

以及展开后的代码。它部分符合卫生宏;由于当前 proc_macro API 的限制,生成的标识符可能是不卫生的,因此使用双下划线(__)作为前缀以降低名称冲突的可能性。

use tylift::tylift;

pub use __kind_Bool::*;
mod __kind_Bool {
    use super::*;
    pub trait Bool: sealed::Sealed {}
    pub struct False(::core::marker::PhantomData<()>);
    impl Bool for False {}
    pub struct True(::core::marker::PhantomData<()>);
    impl Bool for True {}
    mod sealed {
        use super::*;
        pub trait Sealed {}
        impl Sealed for False {}
        impl Sealed for True {}
    }
}

pub(crate) use __kind_Nat::*;
mod __kind_Nat {
    use super::*;
    pub trait Nat: sealed::Sealed {}
    pub struct Zero(::core::marker::PhantomData<()>);
    impl Nat for Zero {}
    pub struct Succ<T0: Nat>(::core::marker::PhantomData<(T0)>);
    impl<T0: Nat> Nat for Succ<T0> {}
    mod sealed {
        use super::*;
        pub trait Sealed {}
        impl Sealed for Zero {}
        impl<T0: Nat> Sealed for Succ<T0> {}
    }
}

use __kind_BinaryTree::*;
mod __kind_BinaryTree {
    use super::*;
    pub trait BinaryTree: sealed::Sealed {}
    pub struct Leaf(::core::marker::PhantomData<()>);
    impl BinaryTree for Leaf {}
    pub struct Branch<T0: BinaryTree, T1: Nat, T2: BinaryTree>(
        ::core::marker::PhantomData<(T0, T1, T2)>,
    );
    impl<T0: BinaryTree, T1: Nat, T2: BinaryTree> BinaryTree for Branch<T0, T1, T2> {}
    mod sealed {
        use super::*;
        pub trait Sealed {}
        impl Sealed for Leaf {}
        impl<T0: BinaryTree, T1: Nat, T2: BinaryTree> Sealed for Branch<T0, T1, T2> {}
    }
}

pub mod Power {
    use super::*;
    pub trait Power: sealed::Sealed {}
    pub struct On(::core::marker::PhantomData<()>);
    impl Power for On {}
    pub struct Off(::core::marker::PhantomData<()>);
    impl Power for Off {}
    mod sealed {
        use super::*;
        pub trait Sealed {}
        impl Sealed for On {}
        impl Sealed for Off {}
    }
}

pub(crate) mod direction {
    use super::*;
    pub trait Direction: sealed::Sealed {}
    /// Higher and higher!
    pub struct Up(::core::marker::PhantomData<()>);
    impl Direction for Up {}
    /// Lower and lower...
    pub struct Down(::core::marker::PhantomData<()>);
    impl Direction for Down {}
    mod sealed {
        use super::*;
        pub trait Sealed {}
        impl Sealed for Up {}
        impl Sealed for Down {}
    }
}

手动编写类型级别函数

从类型 BoolBool 的类型级别函数 Not(在上一个部分中定义的类型)

type Not<B> = <B as NotImpl>::Result;

trait NotImpl: Bool { type Result: Bool; }
impl NotImpl for False { type Result = True; }
impl NotImpl for True { type Result = False; }

从两个 NatNat 的类型级别函数 Add(在上一个部分中定义的类型)

type Add<N, M> = <N as AddImpl<M>>::Result;

trait AddImpl<M: Nat>: Nat { type Result: Nat }
impl<M: Nat> AddImpl<M> for Zero { type Result = M; }
impl<N: Nat, M: Nat> AddImpl<M> for Succ<N>
// where clause necessary because the type system does not know that
// the trait is sealed (only the module system knows)
where N: AddImpl<Succ<M>>
{
    type Result = Add<N, Succ<M>>;
}

tylift 与 Const Generics 的比较

此 crate 与 const generics 相比的优势

  • 递归类型,这些类型目前无法用 const generics 来表示。后者还需要 显式装箱
  • 与旧版 rust 版本的兼容性

显然,这些并不是 非常 有说服力的论据。请将此 crate 视为一个 研究,而不是有价值的东西。也许你可以从其代码中学习到一些东西。

缺点:

  • 需要额外的依赖项(tylift),并且对 syn 有重的传递依赖
  • 较差的工具支持
  • 与兼容 const generics 的 const fn 相比,类型级别的函数非常复杂,参见const 可评估检查

未来计划

  • 用更合理的示例替换入门示例
  • 创建测试
  • 添加其他功能,例如
    • 一个属性,用于将函数提升到类型级别
    • 生成实现函数
  • 一旦 proc_macro_diagnostic 变得稳定,就移除功能门 span_errors

依赖项

~1.5MB
~36K SLoC