#type-level #values #user #field #macro #macro-derive #parameters

无std 程序+库 derive_type_level_lib

宏 1.1 derive 宏用于类型级别值及其使用者

3 个不稳定版本

使用旧的 Rust 2015

0.1.0 2018年11月20日
0.0.21 2018年10月3日
0.0.20 2018年10月3日

#38#type-level


3 个crate中使用

MIT/Apache

210KB
5K SLoC

Build Status

声明和使用类型级别值和函数的包。

库功能

此库提供以下(非穷举列表)类型级别功能

  • 派生枚举和结构的TypeLevel,并对某些核心数据类型进行操作。

  • 声明类型级别函数,包括一些核心数据类型方法的等价物。

  • 收集特质/函数。

  • 其他控制流操作:Panic/断言/If/模式匹配。

  • 字段特质/函数(设置/获取/映射字段)。

  • 整数运算(除了在typenum中提供的外)。

  • 转换特质/函数,在类型级别值和运行时值之间以及它们之间进行转换。

  • 包装操作:unwrap/AndThen/OrElse/IntoInner等。

  • 函数适配器/组合器。

此库为使用类型级别值参数的类型提供以下功能

  • 使用(受类型限制的)类型级别函数修改类型级别值参数,即使类型是引用。

文档

有关API文档,请访问此处。或者如果您已将其包含在crate中,请使用cargo doc --open

有关除单个项和模块的文档以外的文档,包括对TypeLevelMutConstValue derive宏的文档,请访问docs子模块(在type_level_values的文档中)。

示例crate

有关使用type_level库的示例,请阅读指南(也可通过cargo doc --open获取),或查看type_level_examples crate。

最低支持的Rust版本

此包支持 Rust 1.20 以后的 Rust。使用构建脚本在 Rust 1.20 之后启用功能。

这个库支持回退到1.20版本,因为其他基本库也是这样做的,如果这没有必要,请搜索/创建一个问题,争论应该支持哪个版本。

无标准库支持

要在无标准库环境中使用type_level_values,请禁用默认特性。

这个crate几乎没有任何需要标准库(而不是核心库)的功能,它默认启用,以便不了解核心库的用户不需要通过特性来启用需要std的项目。

Cargo 特性

"std": 启用标准库支持,否则使用核心库。默认启用。

"serde": 启用serde支持。默认启用。

"large_tlist": 启用固定大小的实现,以支持最多32个元素的类型列表,而不是16个元素。

示例

假设我们想实现一个特殊的俄罗斯方块游戏,其中我们只有有限的方块(俄罗斯方块),目标是获得最高分数。

在这里,我们实现了一个类型安全的构建器,它跟踪类型系统中每个字段的初始化。

这只是一个示例,这个库不提供为构建器提供 derive 的功能(尽管依赖项的crate可以做到这一点)。

Cargo.toml

type_level_values={version = "0.1"}
derive_type_level={version = "0.1"}

main.rs


// For the 2018 edition uncomment the next lines.
// use derive_type_level::{TypeLevel,MutConstValue};
// use type_level_values::{tlist,mutator_fn};

use type_level_values::prelude::*;
use type_level_values::field_traits::{SetField,SetField_};

fn main(){    
    let pieces=TetrisBuilder::new()
        .l_pieces(10)
        .i_pieces(20)
        .z_pieces(30)
        .s_pieces(40)
        .o_pieces(50)
        .build();

    assert_eq!(
        pieces,
        TetrisPieces{
            l_pieces:10,
            i_pieces:20,
            z_pieces:30,
            s_pieces:40,
            o_pieces:50,
        }
    )
}

///////////////////////////////////////////////////////////////////

macro_rules! declare_setter {( $($field:ident),* $(,)* ) => {


    /// This is the type we are trying to build,
    /// it represents the ammount of tetris pieces left in a special form of tetris. 
    #[derive(Clone, Debug,PartialEq)]
    pub struct TetrisPieces{
        $( $field:usize, )*
    }

    // This creates the type-level equivalent of FieldInitialization in the 
    // type_level_FieldInitialization module,requiring us to reexport what we need.
    #[derive(TypeLevel)]
    //This reexports the type-level equivalents of InitField/UninitField
    #[typelevel(reexport(Variants))]
    pub enum FieldInitialization{
        InitField,  
        UninitField,
    }

    // This derive macro creates the type-level equivalent of InitializedFields in the 
    // type_level_InitializedFields module,requiring us to reexport what we need.
    #[derive(TypeLevel)]
    #[typelevel(
        derive(ConstEq,ConstOrd),
        //This reexports ConstInitializeFields
        reexport(Struct), 
    )]
    pub struct InitializedFields{
        $( pub $field:FieldInitialization, )*
    }

    /// We manually reexport the field accessors submodule 
    /// as if_field to avoid name colisions
    pub use self::type_level_InitializedFields::{
        fields as if_field,
    };

    /**
    This is the ConstInitializedFields we start with.
    
    `InitializedFields_Uninit` is the uninitialized version of ConstInitializedFields,
    which we must initialize to use.

    We use the special field accessor `All` to 
    initialize all the fields with the passed value (which in this case is `UninitField`).
    */
    pub type AllUninitialized=SetField<
        InitializedFields_Uninit,
        if_field::All,
        UninitField
    >;

    /**
    This is the ConstInitializedFields required to build the TetrisPieces.

    This is an alternate way to initialize all the fields,
    allowing us to pass any value for fields
    while ensuring that all fields are initialized.
    */
    pub type AllInitialized=Construct<
        InitializedFields_Uninit,
        tlist!(
            $( (if_field::$field , InitField) ,)*
        )
    >;


    /**
    We declare a datatype which uses a ConstValue-parameter `C` (type-level-value=ConstValue).
    
    Note that the type we use in the rest of the example is 
    `TetrisBuilder` not `__TetrisBuilder`.

    In the future MutConstValue might be superceded (leaving it for pre Rust 1.30 users) 
    by a proc-macro attribute so as to not require a dummy type declaration.
    */
    #[derive(MutConstValue)]
    #[mcv(
        doc="These are the docs for TetrisBuilder_Ty.",
        derive(Clone, Debug),
        Type = "TetrisBuilder",
        ConstValue = "C",
    )]
    pub struct __TetrisBuilder<C>{
        $( $field:Option<usize>, )*
        initialization:ConstWrapper<C>,
    }

    impl TetrisBuilder< AllUninitialized >{
        fn new()->Self {
            TetrisBuilder::default()
        }
    }

    // implementing this on TetrisBuilder<AllUninitialized> caused an internal compiler error,
    // so I just use TypeIdentity to alias AllUninitialized into I.
    impl<I> Default for TetrisBuilder< I >
    where AllUninitialized:TypeIdentity<Type=I>
    {
        fn default()->Self{
            Self{
                $( $field:None, )*
                initialization:ConstWrapper::NEW,
            }
        }
    }

    mod builder_internal{
        
        use super::*;
        
        /// This declares a Type-level function which is allowed to mutate the 
        /// ConstValue-parameter `C` of `TetrisBuilder<C>`.
        mutator_fn!{
            type This[C]=(TetrisBuilder<C>)

            // The AllowedSelf type here determines whether the function is allowed to 
            // mutate C for a value/reference/mutable-reference of TetrisBuilder<C>.
            // For some functions on some types it may be valid to 
            // use some combination of the 3.
            type AllowedSelf=(allowed_self_constructors::ByVal)

            /**
            This is the function,note that we must declare generic parameters 
            for the function inside `[..]`instead of `<..>`,
            this is mostly for implementation simplicity.
            */
            fn InitializeField[I,Field](I,Field)
            // The `[..]` is here to make this easier to parse.
            where [ I:SetField_<Field,InitField,Output=Out>, ]
            { 
                // `let` here declares a type variable,which can be initialized anywhere. 
                let Out;
                // This is the return value of the function,like in regular Rust.
                Out 
            }
        }

        impl<C> TetrisBuilder< C >{
            $(
                // Here we initialize the field and set the 
                // same field on the `C` type parameter (a `ConstInitializedFields<..>`) 
                // as initialized.
                //
                // The `__OutSelf` here is how we emulate "output" types,
                // this is simpler than many alternatives.
                pub fn $field<__OutSelf>(mut self,value:usize)->__OutSelf
                where 
                    Self:MCPBounds<InitializeField,if_field::$field,NextSelf=__OutSelf>
                {
                    self.$field=Some(value);
                    // The `::T` is an associated constant defined in core_extensions::SelfOps,
                    // which allows us to emulate passing types as regular parameters.
                    self.mutparam(InitializeField::NEW,if_field::$field::T)
                }

            )*
        }
    }


    // If this impl block were on TetrisBuilder<AllInitialized>
    // it would just say that the build method does not exist
    impl<C> TetrisBuilder< C >{
        fn build(self)->TetrisPieces
        // TypeIdentity is used here to assert that C and AllInitialized are the same type.
        where C:TypeIdentity<Type= AllInitialized >
        {
            TetrisPieces{
                $( $field:self.$field.unwrap(), )*
            }
        }

    }
}}


declare_setter!{
    l_pieces,
    i_pieces,
    z_pieces,
    s_pieces,
    o_pieces 
}


阅读错误信息

如果我们取消注释 .o_pieces(50) 并尝试构建代码,我们会得到一个相当难以解码的错误信息,使用 [^\(\)\[\]{}>`<,= ]+:: 正则表达式来删除错误信息中的垃圾,它应该产生类似这样的结果

error[E0271]: type mismatch resolving `<ConstInitializedFields<InitField, InitField, InitField, InitField, UninitField<o_pieces>> as SetField_<o_pieces, InitField>>::Output == ConstInitializedFields<InitField, InitField, InitField, InitField, UninitField>`
  --> type_level_examples\src\playground_01.rs:11:10
   |
11 |         .build();
   |          ^^^^^ expected struct `InitField`, found struct `UninitField`
   |
   = note: expected type `ConstInitializedFields<_, _, _, _, InitField>`
              found type `ConstInitializedFields<_, _, _, _, UninitField>`
   = note: required because of the requirements on the impl of `TypeFn_<(ConstInitializedFields<InitField, InitField, InitField, InitField, UninitField<o_pieces>>, (o_pieces, InitField))>` for `SetFieldValuePair`
   <some notes elided to shorten this example error message>

如果我们阅读第一条注释,我们会看到它说最后一个字段未初始化,这意味着我们忘记初始化 o_pieces(我们声明的最后一个字段),所以在构建之前添加对 o_pieces 方法的调用,它应该可以编译无误。

错误是由这个约束引起的 C:TypeIdentity<Type= AllInitialized >,它断言 C 和 AllInitialized 必须是相同的类型。

许可证

type_level 根据您的选择受以下任一许可证的许可:

Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)

贡献

除非您明确声明,否则,您提交给 type_level 的任何贡献,根据 Apache-2.0 许可证的定义,应按上述方式双重许可,不得附加任何其他条款或条件。

依赖项

~5.5MB
~108K SLoC