[][src]Macro structural::structural_alias

macro_rules! structural_alias {
    ( $($everything:tt)* ) => { ... };
}

The structural_alias macro defines a trait alias for multiple field accessors.

Syntax


type Foo_S=TS!("a TStr");
type Bar_S=TS!("another TStr");

structural_alias!{
    #[struc( /* attributes detailed in the attributes section */ )]
    pub trait Foo<'a,T:Copy,V,F>:SuperTrait
    where
        T:SuperTrait
    {
             a:u32,
        ref  b:T,
        mut  c:i64,
        move d:String,
        mut move e:String,
        i:impl Bar,

        /////////////////////
        // Variants
        A{
            foo:u32,
            ref bar:u64,
        },

        ref B(
            String,
            mut Vec<()> // The last `,` is optional
        ),

        mut C(
            u64,
            mut move u64,
        ),

        move D{
            bar:&'static str
        },

        mut move E{
            baz:&'static str,
            ref bam:&'static [u8],
        },

        // The name of the variant is determined by the V type parameter (a TStr).
        <V>,

        // The name of the field is determined by the F type parameter (a TStr).
        <F>:u32,

        // The name of the variant is determined by the Foo_S type alias (a TStr).
        <Foo_S>,

        // The name of the field is determined by the Bar_S type alias (a TStr).
        <Bar_S>:&'static str,

        /////////////////////
        // Extension method
        fn hello(){}

        /////////////////////
        // Extension constant
        const FOO:usize=0;
    }
}

Outside of the {...} the trait syntax is the same as the regular one,with the same meaning.

Inside the {...} is a list of fields,variants,methods,and constants, in any order. The fields and variants get turned into supertraits of Foo. The methods and constants turn into defaulted methods and defaulted constants in Foo.

Fields

  • a:u32: Corresponds to the IntoFieldMut<FP!(a),Ty=u32> trait, allowing shared,mutable,and by value access to the field.

  • ref b:T: Corresponds to the GetField<FP!(b),Ty=T> shared reference field accessor trait.

  • mut c:i64: Corresponds to the GetFieldMut<FP!(c),Ty=i64> mutable reference field accessor trait (which itself implies GetField).

  • move d:String: Corresponds to the IntoField<FP!(d),Ty=String> by value field accessor trait (which itself implies GetField).

  • mut move e:String: Corresponds to the IntoFieldMut<FP!(e),Ty=String> trait, allowing shared,mutable,and by value access to the field.

  • i:impl Bar: Corresponds to the IntoFieldMut<FP!(i),Ty:Bar> trait, allowing shared,mutable,and by value access to a field that implements the Bar trait.
    This requires the nightly_impl_fields or impl_fields cargo feature.

Variants

Variants follow the same pattern as fields regarding access (<none>/ref/mut/move/mut move).

// The `mut` here defines the default access for fields of the variant,
// if none had been specified it would be equivalent to `mut move`.
mut Foo{
    // This is equivalent to `mut foo:String`,`mut` is inherited from the variant.
    // Corresponds to `GetVariantFieldMut<TS!(Foo),TS!(foo),Ty= String>`
    foo:String,
    // Corresponds to `GetVariantField<TS!(Foo),TS!(bar),Ty= u32>`
    ref bar:u32,
    // Corresponds to `IntoVariantFieldMut<TS!(Foo),TS!(baz),Ty= Vec<u32>>`
    mut move baz:Vec<u32>,
},

Aside from the bounds of the fields, variants add an IsVariant bound to query whether the enum is currently that variant.

Functions and constants

You can define defaulted functions and constants,which are defined directly in the trait, and cannot be overriden.

Attributes

These are attributes for each individual trait declared using this macro.

#[struc(debug_print)]

Causes a compiletime error,printing the generated code for a trait.

#[struc(no_docs)]

Removes the docs for the generated trait(s).

The documentation describes variants and fields accessor traits that this trait aliases.

#[struc(exhaustive_enum)]

Makes the structural alias require an enum that has the same variants (it can't have any more variants), with at least the required fields.

Without this attribute,an enum can also have a superset of the required variants.

#[struc(and_exhaustive_enum)]

Generates an additional trait alias,with this trait as a supertarit, also requiring an exhaustive enum as described in the docs for #[struc(exhaustive_enum)].

Variants of the attribute:

  • #[struc(and_exhaustive_enum)]: The generated trait is named <the_name_of_this_trait>_Exhaustive

  • #[struc(and_exhaustive_enum(suffix="ident"))]: The generated trait is named <the_name_of_this_trait><suffix_parameter>

  • #[struc(and_exhaustive_enum(name="ident"))] Uses the name parameter as the name of the generated trait

Without any attributes,an enum can have a superset of the required variants.

Supertraits

Structural aliases as supertraits

Structural aliases are regular traits, so you can use them as supertraits in your own traits.

The defaulted methods in MyTrait could move to Fields if MyTrait had a blanket impl, or because the defaulted methods are not supposed to be overriden.

use structural::{StructuralExt,structural_alias,fp};

structural_alias!{
    trait Fields{
        ref foo:usize,
        ref bar:String,
    }
}

trait MyTrait:Fields{
    fn multiply_foo(&self,n:usize)->usize{
        n * self.field_(fp!(foo))
    }
    fn print_bar(&self){
        println!("{}", self.field_(fp!(bar)) );
    }
}


Same field names

Structural aliases can have other structural aliases as supertraits, even ones with the same fields/variants.

An exception is when the structural alias is for exhaustive enums, using the #[struc(*exaustive_enum)] attributes, where subtraits cannot add variants,but can add fields.

In this example:

use structural::structural_alias;

structural_alias!{
    trait Point<T>{
        move x:T,
        move y:T,
    }

    trait Rectangle<T>:Point<T>{
        ref x:T,
        ref y:T,
        ref w:T,
        ref h:T,
    }
}

It is legal to repeat the x and y fields in subtraits, and those fields get the most permissive access specified, which here is shared and by value access to both x and y.


It is not legal is to redeclare the field with an incompatible type:

This example deliberately fails to compile
use structural::structural_alias;

structural_alias!{
    trait Point<T>{
        x:T,
        y:T,
    }

    trait Rectangle<T>:Point<T>{
        x:usize,
        y:T,
        w:T,
        h:T,
    }
}

impl Trait fields

This requires the nightly_impl_fields cargo feature (or impl_fields if associated type bounds stabilized after the latest release).

You can declare a field with impl Bar as its type to declare that the field implements Bar,without specifying a particular type.

Using impl Trait fields makes a Foo structural alias unusable as a dyn Foo.

Example

This demonstrates using impl trait fields.

This example is not tested
// Remove this if associated type bounds (eg: `T: Iterator<Item: Debug>`)
// work without it.
#![feature(associated_type_bounds)]

use structural::{structural_alias,fp,make_struct,StructuralExt};

structural_alias!{
    trait Foo{
        foo:impl Bar,
    }

    trait Bar{
        dimension:impl Dim<u32>,
    }

    trait Dim<T>{
        width:T,
        height:T,
    }
}

fn with_foo(this:&impl Foo){
    let dim=this.field_(fp!(foo.dimension));
    assert_eq!( dim.field_(fp!(width)), &200 );
    assert_eq!( dim.field_(fp!(height)), &201 );
}


fn main(){
    with_foo(&make_struct!{
        foo:make_struct!{
            dimension:make_struct!{
                width:200,
                height:201,
            }
        }
    });
}

Examples

Defining a Point trait alias

use structural::{structural_alias,fp,StructuralExt,Structural};

use core::{
    cmp::PartialEq,
    fmt::{Debug,Display},
};


structural_alias!{
    trait Point<T>{
        // Using `ref` because we just want to read the fields
        ref x:T,
        ref y:T,
    }
}

fn print_point<T,U>(value:&T)
where
    T:Point<U>,
    U:Debug+Display+PartialEq,
{
    // This gets references to the `x` and `y` fields.
    let (x,y)=value.fields(fp!(x,y));
    assert_ne!(x,y);
    println!("x={} y={}",x,y);
}

fn main(){

    print_point(&Point3D{ x:100, y:200, z:6000 });

    print_point(&Rectangle{ x:100, y:200, w:300, h:400 });

    print_point(&Entity{ x:100.0, y:200.0, id:PersonId(0xDEAD) });

}



#[derive(Structural)]
struct Point3D<T>{
    pub x:T,
    pub y:T,
    pub z:T,
}

#[derive(Structural)]
struct Rectangle<T>{
    pub x:T,
    pub y:T,
    pub w:T,
    pub h:T,
}

#[derive(Structural)]
struct Entity{
    pub id:PersonId,
    pub x:f32,
    pub y:f32,
}


Defining a trait aliases with all accessibilities

use structural::{
    structural_alias,
    fp,
    StructuralExt,
};

structural_alias!{
    trait Person{
        // shared,mutable,and by value access to the field)
        id:PersonId,

        // shared access (a & reference to the field)
        ref name:String,

        // mutable access (a &mut reference to the field),as well as shared access.
        mut friends:Vec<PersonId>,

        // by value access to the field (as well as shared)
        move candy:Candy,

        // shared,mutable,and by value access to the field)
        mut move snack:Snack,
    }
}





Exhaustive enums

This demonstrates how you can define a trait alias for exhaustive enums, along with extension methods.


use std::path::PathBuf;
use dependency::YesWith;

#[derive(Structural)]
#[struc(no_trait)]
enum WasFileFound{
    Yes(PathBuf),
    No,
}

fn find_file(name:&str)->WasFileFound{
    WasFileFound::Yes( PathBuf::from(name) )
}

fn main(){
    let filename="hello";

    // The `into_yes` method comes from the BooleanEnum trait
    if let Some(file)= find_file(filename).into_yes() {
        println!("Found '{}' at this path: {} ",filename,file.display());
    }
}


///////////////////////////////////////////////////////////////////////////////
////                    In a dependency
///////////////////////////////////////////////////////////////////////////////

use structural::{
    fp,structural_alias,switch,
    field::{GetVariantFieldType,IntoVariantField},
    StructuralExt,
    Structural,
};

structural_alias!{
    #[struc(exhaustive_enum)]
    pub trait BooleanEnum{
        Yes{},
        No{},

        fn is_yes(&self)->bool{
            switch!{ref self;
                Yes=>true,
                No=>false
            }
        }

        fn is_no(&self)->bool{
            switch!{ref self;
                No=>true,
                Yes=>false
            }
        }
    }

    pub trait YesWith<T>: BooleanEnum {
        Yes(T),

        fn into_yes(self)->Option<T>
        where
            Self: Sized
        {
            self.into_field(fp!(::Yes.0))
        }
    }
}

Generic Variant Name

This example demonstrates declaring a structural alias with a generic variant name.


use structural::{structural_alias, switch, Structural, TS};

use std::cmp::Ordering;

#[derive(Structural)]
enum Enum{
    Foo(u32),
    Bar(&'static str),
}

#[derive(Structural)]
enum OtherEnum{
    Foo(u32,Ordering),
    Bar(&'static str,u64),
    Baz,
}

fn main(){
    hello(Enum::Foo(0));

    hello(Enum::Bar("what"));

    // The `Ordering` field is ignored
    hello(OtherEnum::Foo(0, Ordering::Less));

    // The integer field is ignored
    hello(OtherEnum::Bar("what", 1000));

    // `OtherEnum::Baz` is just ignored
    hello(OtherEnum::Baz);
}

fn hello(this:impl Single_VSI<TS!(Foo),u32> + Single_VSI<TS!(Bar),&'static str>){
    switch!{ref this;
        Foo(&number)=>assert_eq!(number,0),
        Bar(&string)=>assert_eq!(string,"what"),
        _=>{}
    }
}

structural_alias!{
    // This trait is equivalent to what the `Structural` derive would generate for
    // this type: `struct Single<T>(pub T);`
    trait Single_VSI<V,T>{
        // `V` is the type parameter(a TStr),it determines the name of the variant.
        <V>(T)
    }
}