#type-level

无std derive_type_level

为type_level值及其用户提供的宏1.1

3个不稳定版本

使用旧的Rust 2015

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

#41 in #type-level


用于 2 crates

MIT/Apache

215KB
5K SLoC

Build Status

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

库特性

此库提供以下(非详尽列表)类型级特性

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

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

  • 收集特性/函数。

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

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

  • 整数操作(除了在typenum中提供的外)。

  • 转换特性/函数,包括类型级值和运行时值之间的转换。

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

  • 函数适配器/组合器。

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

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

文档

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

对于除单个项和模块文档之外的其他文档,包括对TypeLevelMutConstValue派生宏的文档,请访问docs子模块(在type_level_values的文档中)。

示例crate

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

最低支持的Rust版本

此包支持Rust 1.20及更高版本。使用构建脚本启用Rust 1.20之后的特性。

此库支持Rust 1.20及更高版本,因为其他基本库也是如此,如果这不需要,请搜索/创建一个论点,说明应该支持哪个版本。

无std支持

要在无std上下文中使用type_level_values,请禁用默认功能。

此crate几乎没有需要标准库(而不是core)的功能,默认启用是为了让不了解core库的用户无需传递功能以启用需要std的项目。

Cargo功能

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

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

"large_tlist": 启用固定大小为32个元素的类型列表的impl,而不是16个元素。

示例

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

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

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

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
~107K SLoC